Clean up and consolidate destructuring transform

This commit is contained in:
Ron Buckton 2016-10-25 17:25:28 -07:00
parent 6453fde400
commit 86091d7217
21 changed files with 592 additions and 446 deletions

View File

@ -1007,7 +1007,7 @@ namespace ts {
currentFlow = finishFlowLabel(preFinallyLabel);
bind(node.finallyBlock);
// if flow after finally is unreachable - keep it
// otherwise check if flows after try and after catch are unreachable
// otherwise check if flows after try and after catch are unreachable
// if yes - convert current flow to unreachable
// i.e.
// try { return "1" } finally { console.log(1); }
@ -3077,7 +3077,7 @@ namespace ts {
case SyntaxKind.SpreadElementExpression:
// This node is ES6 syntax, but is handled by a containing node.
transformFlags |= TransformFlags.ContainsSpreadElementExpression;
transformFlags |= TransformFlags.AssertES2015 | TransformFlags.ContainsSpreadElementExpression;
break;
case SyntaxKind.SuperKeyword:

View File

@ -2025,9 +2025,7 @@ namespace ts {
for (let i = 0; i < lines.length; i++) {
const line = indentation ? lines[i].slice(indentation) : lines[i];
if (line.length) {
if (i > 0) {
writeLine();
}
writeLine();
write(line);
}
}

View File

@ -1699,6 +1699,8 @@ namespace ts {
}
}
// Utilities
export interface CallBinding {
target: LeftHandSideExpression;
thisArg: Expression;
@ -2733,7 +2735,9 @@ namespace ts {
*/
export function addEmitHelper<T extends Node>(node: T, helper: EmitHelper): T {
const emitNode = getOrCreateEmitNode(node);
emitNode.helpers = append(emitNode.helpers, helper);
if (!contains(emitNode.helpers, helper)) {
emitNode.helpers = append(emitNode.helpers, helper);
}
return node;
}

View File

@ -3,137 +3,146 @@
/*@internal*/
namespace ts {
type EffectiveBindingElement
= VariableDeclaration
| ParameterDeclaration
| BindingElement
| ObjectLiteralElementLike
| Expression
;
type EffectiveObjectBindingPattern
= ObjectBindingPattern
| ObjectLiteralExpression
;
type EffectiveArrayBindingPattern
= ArrayBindingPattern
| ArrayLiteralExpression
;
type EffectiveBindingPattern
= EffectiveObjectBindingPattern
| EffectiveArrayBindingPattern
;
type EffectiveBindingTarget
= EffectiveBindingPattern
| Expression
;
/**
* Flattens a destructuring assignment expression.
* Flattens a DestructuringAssignment or a VariableDeclaration to an expression.
*
* @param root The destructuring assignment expression.
* @param node The node to flatten.
* @param needsValue Indicates whether the value from the right-hand-side of the
* destructuring assignment is needed as part of a larger expression.
* destructuring assignment is needed as part of a larger expression.
* @param createAssignmentCallback A callback used to create an assignment expression.
* @param recordTempVariable A callback used to record new temporary variables.
* @param visitor An optional visitor to use to visit expressions.
* @param visitor An optional visitor used to visit default value initializers of binding patterns.
*/
export function flattenDestructuringAssignment(
context: TransformationContext,
node: BinaryExpression,
export function flattenDestructuringToExpression(
node: VariableDeclaration | DestructuringAssignment,
needsValue: boolean,
createAssignmentCallback: (target: Expression, value: Expression, location?: TextRange) => Expression,
recordTempVariable: (node: Identifier) => void,
visitor?: (node: Node) => VisitResult<Node>): Expression {
if (isEmptyObjectLiteralOrArrayLiteral(node.left)) {
const right = node.right;
if (isDestructuringAssignment(right)) {
return flattenDestructuringAssignment(context, right, needsValue, recordTempVariable, visitor);
}
else {
return node.right;
}
}
let location: TextRange = node;
let value = node.right;
let value: Expression;
if (isDestructuringAssignment(node)) {
value = node.right;
while (isEmptyObjectLiteralOrArrayLiteral(node.left)) {
if (isDestructuringAssignment(value)) {
location = node = value;
value = node.right;
}
else {
return value;
}
}
}
const expressions: Expression[] = [];
if (needsValue) {
// If the right-hand value of the destructuring assignment needs to be preserved (as
// is the case when the destructuring assignmen) is part of a larger expression),
// then we need to cache the right-hand value.
//
// The source map location for the assignment should point to the entire binary
// expression.
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment, visitor);
}
else if (nodeIsSynthesized(node)) {
// Generally, the source map location for a destructuring assignment is the root
// expression.
//
// However, if the root expression is synthesized (as in the case
// of the initializer when transforming a ForOfStatement), then the source map
// location should point to the right-hand value of the expression.
location = value;
if (value) {
value = visitNode(value, visitor, isExpression);
if (needsValue) {
// If the right-hand value of the destructuring assignment needs to be preserved (as
// is the case when the destructuring assignment is part of a larger expression),
// then we need to cache the right-hand value.
//
// The source map location for the assignment should point to the entire binary
// expression.
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, emitTempVariableAssignment, location);
}
else if (nodeIsSynthesized(node)) {
// Generally, the source map location for a destructuring assignment is the root
// expression.
//
// However, if the root expression is synthesized (as in the case
// of the initializer when transforming a ForOfStatement), then the source map
// location should point to the right-hand value of the expression.
location = value;
}
}
flattenDestructuring(node, value, location, emitAssignment, emitTempVariableAssignment, visitor);
flattenEffectiveBindingElement(node, value, isDestructuringAssignment(node), emitAssignment, emitTempVariableAssignment, visitor, location);
if (needsValue) {
if (value && needsValue) {
expressions.push(value);
}
const expression = inlineExpressions(expressions);
aggregateTransformFlags(expression);
return expression;
function emitAssignment(name: Identifier, value: Expression, location: TextRange) {
const expression = createAssignment(name, value, location);
return aggregateTransformFlags(inlineExpressions(expressions));
function emitAssignment(target: Expression, value: Expression, location: TextRange, original: Node) {
const expression = createAssignmentCallback(target, value, location);
expression.original = original;
// NOTE: this completely disables source maps, but aligns with the behavior of
// `emitAssignment` in the old emitter.
setEmitFlags(expression, EmitFlags.NoNestedSourceMaps);
aggregateTransformFlags(expression);
expressions.push(expression);
}
function emitTempVariableAssignment(value: Expression, location: TextRange) {
const name = createTempVariable(recordTempVariable);
emitAssignment(name, value, location);
return name;
}
}
/**
* Flattens binding patterns in a parameter declaration.
*
* @param node The ParameterDeclaration to flatten.
* @param value The rhs value for the binding pattern.
* @param visitor An optional visitor to use to visit expressions.
*/
export function flattenParameterDestructuring(
node: ParameterDeclaration,
value: Expression,
visitor?: (node: Node) => VisitResult<Node>) {
const declarations: VariableDeclaration[] = [];
flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment, visitor);
return declarations;
function emitAssignment(name: Identifier, value: Expression, location: TextRange) {
const declaration = createVariableDeclaration(name, /*type*/ undefined, value, location);
const expression = createAssignment(name, value, location);
// NOTE: this completely disables source maps, but aligns with the behavior of
// `emitAssignment` in the old emitter.
setEmitFlags(declaration, EmitFlags.NoNestedSourceMaps);
aggregateTransformFlags(declaration);
declarations.push(declaration);
}
function emitTempVariableAssignment(value: Expression, location: TextRange) {
const name = createTempVariable(/*recordTempVariable*/ undefined);
emitAssignment(name, value, location);
setEmitFlags(expression, EmitFlags.NoNestedSourceMaps);
aggregateTransformFlags(expression);
expressions.push(expression);
return name;
}
}
/**
* Flattens binding patterns in a variable declaration.
* Flattens binding patterns in a VariableDeclaration or a ParameterDeclaration to a VariableDeclaration list.
*
* @param node The VariableDeclaration to flatten.
* @param value An optional rhs value for the binding pattern.
* @param node The node to flatten.
* @param boundValue An optional rhs value for the binding pattern. This value is *not* visited during flattening.
* @param visitor An optional visitor to use to visit expressions.
* @param recordTempVariable An optional callback used to hoist temporary variables rather than
* declaring them inline.
*/
export function flattenVariableDestructuring(
node: VariableDeclaration,
value?: Expression,
visitor?: (node: Node) => VisitResult<Node>,
recordTempVariable?: (node: Identifier) => void) {
const declarations: VariableDeclaration[] = [];
export function flattenDestructuring(
node: VariableDeclaration | ParameterDeclaration,
boundValue?: Expression,
recordTempVariable?: (node: Identifier) => void,
visitor?: (node: Node) => VisitResult<Node>) {
let pendingAssignments: Expression[];
flattenDestructuring(node, value, node, emitAssignment, emitTempVariableAssignment, visitor);
const declarations: VariableDeclaration[] = [];
flattenEffectiveBindingElement(node, boundValue, /*skipInitializer*/ false, emitAssignment, emitTempVariableAssignment, visitor, /*location*/ node);
return declarations;
function emitAssignment(name: Identifier, value: Expression, location: TextRange, original: Node) {
function emitAssignment(name: Expression, value: Expression, location: TextRange, original: Node) {
if (!isIdentifier(name)) {
Debug.failBadSyntaxKind(name, "Identifier expected.");
return;
}
if (pendingAssignments) {
pendingAssignments.push(value);
value = inlineExpressions(pendingAssignments);
@ -145,22 +154,13 @@ namespace ts {
// NOTE: this completely disables source maps, but aligns with the behavior of
// `emitAssignment` in the old emitter.
setEmitFlags(declaration, EmitFlags.NoNestedSourceMaps);
declarations.push(declaration);
aggregateTransformFlags(declaration);
declarations.push(aggregateTransformFlags(setEmitFlags(declaration, EmitFlags.NoNestedSourceMaps)));
}
function emitTempVariableAssignment(value: Expression, location: TextRange) {
const name = createTempVariable(recordTempVariable);
if (recordTempVariable) {
const assignment = createAssignment(name, value, location);
if (pendingAssignments) {
pendingAssignments.push(assignment);
}
else {
pendingAssignments = [assignment];
}
pendingAssignments = append(pendingAssignments, createAssignment(name, value, location));
}
else {
emitAssignment(name, value, location, /*original*/ undefined);
@ -169,244 +169,365 @@ namespace ts {
}
}
function flattenEffectiveBindingElement(
bindingElement: EffectiveBindingElement,
boundValue: Expression | undefined,
skipInitializer: boolean,
emitAssignment: (target: Expression, value: Expression, location: TextRange, original: Node) => void,
emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier,
visitor: ((node: Node) => VisitResult<Node>) | undefined,
location: TextRange) {
if (!skipInitializer) {
const initializer = visitNode(getInitializerOfEffectiveBindingElement(bindingElement), visitor, isExpression);
if (initializer) {
// Combine value and initializer
boundValue = boundValue ? createDefaultValueCheck(boundValue, initializer, location, emitTempVariableAssignment) : initializer;
}
else if (!boundValue) {
// Use 'void 0' in absence of value and initializer
boundValue = createVoidZero();
}
}
const bindingTarget = getTargetOfEffectiveBindingElement(bindingElement);
if (isEffectiveBindingPattern(bindingTarget)) {
const elements = getElementsOfEffectiveBindingPattern(bindingTarget);
const numElements = elements.length;
if (numElements !== 1) {
// For anything other than a single-element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once. Additionally, if we have zero elements
// we need to emit *something* to ensure that in case a 'var' keyword was already emitted,
// so in that case, we'll intentionally create that temporary.
const reuseIdentifierExpressions = !isDeclarationBindingElement(bindingElement) || numElements !== 0;
boundValue = ensureIdentifier(boundValue, reuseIdentifierExpressions, emitTempVariableAssignment, location);
}
if (isEffectiveObjectBindingPattern(bindingTarget)) {
for (const element of elements) {
// Rewrite element to a declaration with an initializer that fetches property
flattenEffectiveBindingElement(
element,
createDestructuringPropertyAccess(
boundValue,
getEffectivePropertyNameOfEffectiveBindingElement(element),
emitTempVariableAssignment),
/*skipInitializer*/ false,
emitAssignment,
emitTempVariableAssignment,
visitor,
/*location*/ element);
}
}
else {
for (let i = 0; i < numElements; i++) {
const element = elements[i];
if (!isOmittedExpression(element)) {
if (!isEffectiveBindingElementWithRest(element)) {
// Rewrite element to a declaration that accesses array element at index i
flattenEffectiveBindingElement(
element,
createElementAccess(boundValue, i),
/*skipInitializer*/ false,
emitAssignment,
emitTempVariableAssignment,
visitor,
/*location*/ element);
}
else if (i === numElements - 1) {
flattenEffectiveBindingElement(
element,
createArraySlice(boundValue, i),
/*skipInitializer*/ false,
emitAssignment,
emitTempVariableAssignment,
visitor,
/*location*/ element);
}
}
}
}
}
else {
emitAssignment(bindingTarget, boundValue, location, /*original*/ bindingElement);
}
}
/**
* Flattens binding patterns in a variable declaration and transforms them into an expression.
*
* @param node The VariableDeclaration to flatten.
* @param recordTempVariable A callback used to record new temporary variables.
* @param createAssignmentCallback An optional callback used to create assignment expressions
* for non-temporary variables.
* @param visitor An optional visitor to use to visit expressions.
* Determines whether the BindingElement-like is a declaration
*/
export function flattenVariableDestructuringToExpression(
node: VariableDeclaration,
recordTempVariable: (name: Identifier) => void,
createAssignmentCallback?: (name: Identifier, value: Expression, location?: TextRange) => Expression,
visitor?: (node: Node) => VisitResult<Node>) {
const pendingAssignments: Expression[] = [];
flattenDestructuring(node, /*value*/ undefined, node, emitAssignment, emitTempVariableAssignment, visitor);
const expression = inlineExpressions(pendingAssignments);
aggregateTransformFlags(expression);
return expression;
function emitAssignment(name: Identifier, value: Expression, location: TextRange, original: Node) {
const expression = createAssignmentCallback
? createAssignmentCallback(name, value, location)
: createAssignment(name, value, location);
emitPendingAssignment(expression, original);
function isDeclarationBindingElement(bindingElement: EffectiveBindingElement): bindingElement is VariableDeclaration | ParameterDeclaration | BindingElement {
switch (bindingElement.kind) {
case SyntaxKind.VariableDeclaration:
case SyntaxKind.Parameter:
case SyntaxKind.BindingElement:
return true;
}
function emitTempVariableAssignment(value: Expression, location: TextRange) {
const name = createTempVariable(recordTempVariable);
emitPendingAssignment(createAssignment(name, value, location), /*original*/ undefined);
return name;
return false;
}
/**
* Gets the initializer of a BindingElement-like element
*/
function getInitializerOfEffectiveBindingElement(bindingElement: EffectiveBindingElement): Expression | undefined {
if (isDeclarationBindingElement(bindingElement)) {
// `1` in `let { a = 1 } = ...`
// `1` in `let { a: b = 1 } = ...`
// `1` in `let { a: {b} = 1 } = ...`
// `1` in `let { a: [b] = 1 } = ...`
// `1` in `let [a = 1] = ...`
// `1` in `let [{a} = 1] = ...`
// `1` in `let [[a] = 1] = ...`
return bindingElement.initializer;
}
function emitPendingAssignment(expression: Expression, original: Node) {
expression.original = original;
if (isPropertyAssignment(bindingElement)) {
// `1` in `({ a: b = 1 } = ...)`
// `1` in `({ a: {b} = 1 } = ...)`
// `1` in `({ a: [b] = 1 } = ...)`
return isAssignmentExpression(bindingElement.initializer, /*excludeCompoundAssignment*/ true)
? bindingElement.initializer.right
: undefined;
}
// NOTE: this completely disables source maps, but aligns with the behavior of
// `emitAssignment` in the old emitter.
setEmitFlags(expression, EmitFlags.NoNestedSourceMaps);
if (isShorthandPropertyAssignment(bindingElement)) {
// `1` in `({ a = 1 } = ...)`
return bindingElement.objectAssignmentInitializer;
}
pendingAssignments.push(expression);
if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) {
// `1` in `[a = 1] = ...`
// `1` in `[{a} = 1] = ...`
// `1` in `[[a] = 1] = ...`
return bindingElement.right;
}
if (isSpreadElementExpression(bindingElement) || isPartiallyEmittedExpression(bindingElement)) {
// Recovery consistent with existing emit.
return getInitializerOfEffectiveBindingElement(bindingElement.expression);
}
}
function flattenDestructuring(
root: VariableDeclaration | ParameterDeclaration | BindingElement | BinaryExpression,
value: Expression,
location: TextRange,
emitAssignment: (name: Identifier, value: Expression, location: TextRange, original: Node) => void,
emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier,
visitor?: (node: Node) => VisitResult<Node>) {
if (value && visitor) {
value = visitNode(value, visitor, isExpression);
/**
* Gets the name of a BindingElement-like element
*/
function getTargetOfEffectiveBindingElement(bindingElement: EffectiveBindingElement): EffectiveBindingTarget {
if (isDeclarationBindingElement(bindingElement)) {
// `a` in `let { a } = ...`
// `a` in `let { a = 1 } = ...`
// `b` in `let { a: b } = ...`
// `b` in `let { a: b = 1 } = ...`
// `{b}` in `let { a: {b} } = ...`
// `{b}` in `let { a: {b} = 1 } = ...`
// `[b]` in `let { a: [b] } = ...`
// `[b]` in `let { a: [b] = 1 } = ...`
// `a` in `let [a] = ...`
// `a` in `let [a = 1] = ...`
// `a` in `let [...a] = ...`
// `{a}` in `let [{a}] = ...`
// `{a}` in `let [{a} = 1] = ...`
// `[a]` in `let [[a]] = ...`
// `[a]` in `let [[a] = 1] = ...`
return <ObjectBindingPattern | ArrayBindingPattern | Identifier>bindingElement.name;
}
if (isBinaryExpression(root)) {
emitDestructuringAssignment(root.left, value, location);
if (isObjectLiteralElementLike(bindingElement)) {
switch (bindingElement.kind) {
case SyntaxKind.PropertyAssignment:
// `b` in `({ a: b } = ...)`
// `b` in `({ a: b = 1 } = ...)`
// `{b}` in `({ a: {b} } = ...)`
// `{b}` in `({ a: {b} = 1 } = ...)`
// `[b]` in `({ a: [b] } = ...)`
// `[b]` in `({ a: [b] = 1 } = ...)`
// `b.c` in `({ a: b.c } = ...)`
// `b.c` in `({ a: b.c = 1 } = ...)`
// `b[0]` in `({ a: b[0] } = ...)`
// `b[0]` in `({ a: b[0] = 1 } = ...)`
return getTargetOfEffectiveBindingElement(bindingElement.initializer);
case SyntaxKind.ShorthandPropertyAssignment:
// `a` in `({ a } = ...)`
// `a` in `({ a = 1 } = ...)`
return bindingElement.name;
}
// no target
return undefined;
}
if (isAssignmentExpression(bindingElement, /*excludeCompoundAssignment*/ true)) {
// `a` in `[a = 1] = ...`
// `{a}` in `[{a} = 1] = ...`
// `[a]` in `[[a] = 1] = ...`
// `a.b` in `[a.b = 1] = ...`
// `a[0]` in `[a[0] = 1] = ...`
return getTargetOfEffectiveBindingElement(bindingElement.left);
}
if (isSpreadElementExpression(bindingElement) || isPartiallyEmittedExpression(bindingElement)) {
// `a` in `[...a] = ...`
return getTargetOfEffectiveBindingElement(bindingElement.expression);
}
// `a` in `[a] = ...`
// `{a}` in `[{a}] = ...`
// `[a]` in `[[a]] = ...`
// `a.b` in `[a.b] = ...`
// `a[0]` in `[a[0]] = ...`
return bindingElement;
}
/**
* Determines whether a BindingElement-like element is a rest element.
*/
function isEffectiveBindingElementWithRest(bindingElement: EffectiveBindingElement) {
switch (bindingElement.kind) {
case SyntaxKind.Parameter:
case SyntaxKind.BindingElement:
// `...` in `let [...a] = ...`
return (<ParameterDeclaration | BindingElement>bindingElement).dotDotDotToken !== undefined;
case SyntaxKind.SpreadElementExpression:
// `...` in `[...a] = ...`
return true;
}
return false;
}
/**
* Gets the property name of a BindingElement-like element
*/
function getEffectivePropertyNameOfEffectiveBindingElement(bindingElement: EffectiveBindingElement) {
switch (bindingElement.kind) {
case SyntaxKind.BindingElement:
// `a` in `let { a: b } = ...`
// `[a]` in `let { [a]: b } = ...`
// `"a"` in `let { "a": b } = ...`
// `1` in `let { 1: b } = ...`
if ((<BindingElement>bindingElement).propertyName) {
return (<BindingElement>bindingElement).propertyName;
}
break;
case SyntaxKind.PropertyAssignment:
// `a` in `({ a: b } = ...)`
// `[a]` in `({ [a]: b } = ...)`
// `"a"` in `({ "a": b } = ...)`
// `1` in `({ 1: b } = ...)`
if ((<PropertyAssignment>bindingElement).name) {
return (<PropertyAssignment>bindingElement).name;
}
break;
}
const target = getTargetOfEffectiveBindingElement(bindingElement);
if (target && isPropertyName(target)) {
return target;
}
Debug.fail("Invalid property name for binding element.");
}
/**
* Determines whether a node is BindingPattern-like
*/
function isEffectiveBindingPattern(node: EffectiveBindingTarget): node is EffectiveBindingPattern {
return isEffectiveObjectBindingPattern(node)
|| isEffectiveArrayBindingPattern(node);
}
/**
* Determines whether a node is ObjectBindingPattern-like
*/
function isEffectiveObjectBindingPattern(node: EffectiveBindingTarget): node is EffectiveObjectBindingPattern {
switch (node.kind) {
case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ObjectLiteralExpression:
return true;
}
return false;
}
/**
* Determines whether a node is ArrayBindingPattern-like
*/
function isEffectiveArrayBindingPattern(node: EffectiveBindingTarget): node is EffectiveArrayBindingPattern {
switch (node.kind) {
case SyntaxKind.ArrayBindingPattern:
case SyntaxKind.ArrayLiteralExpression:
return true;
}
return false;
}
/**
* Gets the elements of a BindingPattern-like name
*/
function getElementsOfEffectiveBindingPattern(name: EffectiveBindingPattern): EffectiveBindingElement[] {
switch (name.kind) {
case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ArrayBindingPattern:
// `a` in `{a}`
// `a` in `[a]`
return name.elements;
case SyntaxKind.ObjectLiteralExpression:
// `a` in `{a}`
return name.properties;
case SyntaxKind.ArrayLiteralExpression:
// `a` in `[a]`
return name.elements;
}
}
/**
* Creates an expression used to provide a default value if a value is `undefined` at runtime.
*/
function createDefaultValueCheck(value: Expression, defaultValue: Expression, location: TextRange, emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier): Expression {
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, emitTempVariableAssignment, location);
return createConditional(
createStrictEquality(value, createVoidZero()),
createToken(SyntaxKind.QuestionToken),
defaultValue,
createToken(SyntaxKind.ColonToken),
value
);
}
/**
* Creates either a PropertyAccessExpression or an ElementAccessExpression for the
* right-hand side of a transformed destructuring assignment.
*
* @param expression The right-hand expression that is the source of the property.
* @param propertyName The destructuring property name.
* @param emitTempVariableAssignment A callback used to emit a temporary variable.
*/
function createDestructuringPropertyAccess(expression: Expression, propertyName: PropertyName, emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier): LeftHandSideExpression {
if (isComputedPropertyName(propertyName)) {
const argumentExpression = ensureIdentifier(propertyName.expression, /*reuseIdentifierExpressions*/ false, emitTempVariableAssignment, /*location*/ propertyName);
return createElementAccess(expression, argumentExpression);
}
else if (isLiteralExpression(propertyName)) {
const argumentExpression = getSynthesizedClone(propertyName);
argumentExpression.text = unescapeIdentifier(argumentExpression.text);
return createElementAccess(expression, argumentExpression);
}
else if (isGeneratedIdentifier(propertyName)) {
const name = getSynthesizedClone(propertyName);
name.text = unescapeIdentifier(name.text);
return createPropertyAccess(expression, name);
}
else {
emitBindingElement(root, value);
}
function emitDestructuringAssignment(bindingTarget: Expression | ShorthandPropertyAssignment, value: Expression, location: TextRange) {
// When emitting target = value use source map node to highlight, including any temporary assignments needed for this
let target: Expression;
if (isShorthandPropertyAssignment(bindingTarget)) {
const initializer = visitor
? visitNode(bindingTarget.objectAssignmentInitializer, visitor, isExpression)
: bindingTarget.objectAssignmentInitializer;
if (initializer) {
value = createDefaultValueCheck(value, initializer, location);
}
target = bindingTarget.name;
}
else if (isBinaryExpression(bindingTarget) && bindingTarget.operatorToken.kind === SyntaxKind.EqualsToken) {
const initializer = visitor
? visitNode(bindingTarget.right, visitor, isExpression)
: bindingTarget.right;
value = createDefaultValueCheck(value, initializer, location);
target = bindingTarget.left;
}
else {
target = bindingTarget;
}
if (target.kind === SyntaxKind.ObjectLiteralExpression) {
emitObjectLiteralAssignment(<ObjectLiteralExpression>target, value, location);
}
else if (target.kind === SyntaxKind.ArrayLiteralExpression) {
emitArrayLiteralAssignment(<ArrayLiteralExpression>target, value, location);
}
else {
const name = getMutableClone(<Identifier>target);
setSourceMapRange(name, target);
setCommentRange(name, target);
emitAssignment(name, value, location, /*original*/ undefined);
}
}
function emitObjectLiteralAssignment(target: ObjectLiteralExpression, value: Expression, location: TextRange) {
const properties = target.properties;
if (properties.length !== 1) {
// For anything but a single element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once.
// When doing so we want to hightlight the passed in source map node since thats the one needing this temp assignment
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment);
}
for (const p of properties) {
if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) {
const propName = <Identifier | LiteralExpression>(<PropertyAssignment>p).name;
const target = p.kind === SyntaxKind.ShorthandPropertyAssignment ? <ShorthandPropertyAssignment>p : (<PropertyAssignment>p).initializer || propName;
// Assignment for target = value.propName should highligh whole property, hence use p as source map node
emitDestructuringAssignment(target, createDestructuringPropertyAccess(value, propName), p);
}
}
}
function emitArrayLiteralAssignment(target: ArrayLiteralExpression, value: Expression, location: TextRange) {
const elements = target.elements;
const numElements = elements.length;
if (numElements !== 1) {
// For anything but a single element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once.
// When doing so we want to hightlight the passed in source map node since thats the one needing this temp assignment
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment);
}
for (let i = 0; i < numElements; i++) {
const e = elements[i];
if (e.kind !== SyntaxKind.OmittedExpression) {
// Assignment for target = value.propName should highligh whole property, hence use e as source map node
if (e.kind !== SyntaxKind.SpreadElementExpression) {
emitDestructuringAssignment(e, createElementAccess(value, createLiteral(i)), e);
}
else if (i === numElements - 1) {
emitDestructuringAssignment((<SpreadElementExpression>e).expression, createArraySlice(value, i), e);
}
}
}
}
function emitBindingElement(target: VariableDeclaration | ParameterDeclaration | BindingElement, value: Expression) {
// Any temporary assignments needed to emit target = value should point to target
const initializer = visitor ? visitNode(target.initializer, visitor, isExpression) : target.initializer;
if (initializer) {
// Combine value and initializer
value = value ? createDefaultValueCheck(value, initializer, target) : initializer;
}
else if (!value) {
// Use 'void 0' in absence of value and initializer
value = createVoidZero();
}
const name = target.name;
if (isBindingPattern(name)) {
const elements = name.elements;
const numElements = elements.length;
if (numElements !== 1) {
// For anything other than a single-element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once. Additionally, if we have zero elements
// we need to emit *something* to ensure that in case a 'var' keyword was already emitted,
// so in that case, we'll intentionally create that temporary.
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ numElements !== 0, target, emitTempVariableAssignment);
}
for (let i = 0; i < numElements; i++) {
const element = elements[i];
if (isOmittedExpression(element)) {
continue;
}
else if (name.kind === SyntaxKind.ObjectBindingPattern) {
// Rewrite element to a declaration with an initializer that fetches property
const propName = element.propertyName || <Identifier>element.name;
emitBindingElement(element, createDestructuringPropertyAccess(value, propName));
}
else {
if (!element.dotDotDotToken) {
// Rewrite element to a declaration that accesses array element at index i
emitBindingElement(element, createElementAccess(value, i));
}
else if (i === numElements - 1) {
emitBindingElement(element, createArraySlice(value, i));
}
}
}
}
else {
emitAssignment(name, value, target, target);
}
}
function createDefaultValueCheck(value: Expression, defaultValue: Expression, location: TextRange): Expression {
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment);
return createConditional(
createStrictEquality(value, createVoidZero()),
createToken(SyntaxKind.QuestionToken),
defaultValue,
createToken(SyntaxKind.ColonToken),
value
);
}
/**
* Creates either a PropertyAccessExpression or an ElementAccessExpression for the
* right-hand side of a transformed destructuring assignment.
*
* @param expression The right-hand expression that is the source of the property.
* @param propertyName The destructuring property name.
*/
function createDestructuringPropertyAccess(expression: Expression, propertyName: PropertyName): LeftHandSideExpression {
if (isComputedPropertyName(propertyName)) {
return createElementAccess(
expression,
ensureIdentifier(propertyName.expression, /*reuseIdentifierExpressions*/ false, /*location*/ propertyName, emitTempVariableAssignment)
);
}
else if (isLiteralExpression(propertyName)) {
const clone = getSynthesizedClone(propertyName);
clone.text = unescapeIdentifier(clone.text);
return createElementAccess(expression, clone);
}
else {
if (isGeneratedIdentifier(propertyName)) {
const clone = getSynthesizedClone(propertyName);
clone.text = unescapeIdentifier(clone.text);
return createPropertyAccess(expression, clone);
}
else {
return createPropertyAccess(expression, createIdentifier(unescapeIdentifier(propertyName.text)));
}
}
const name = createIdentifier(unescapeIdentifier(propertyName.text));
return createPropertyAccess(expression, name);
}
}
@ -417,26 +538,20 @@ namespace ts {
*
* @param value the expression whose value needs to be bound.
* @param reuseIdentifierExpressions true if identifier expressions can simply be returned;
* false if it is necessary to always emit an identifier.
* @param location The location to use for source maps and comments.
* false if it is necessary to always emit an identifier.
* @param emitTempVariableAssignment A callback used to emit a temporary variable.
* @param visitor An optional callback used to visit the value.
* @param location The location to use for source maps and comments.
*/
function ensureIdentifier(
value: Expression,
reuseIdentifierExpressions: boolean,
location: TextRange,
emitTempVariableAssignment: (value: Expression, location: TextRange) => Identifier,
visitor?: (node: Node) => VisitResult<Node>) {
location: TextRange) {
if (isIdentifier(value) && reuseIdentifierExpressions) {
return value;
}
else {
if (visitor) {
value = visitNode(value, visitor, isExpression);
}
return emitTempVariableAssignment(value, location);
}
}

View File

@ -1,5 +1,6 @@
/// <reference path="../factory.ts" />
/// <reference path="../visitor.ts" />
/// <reference path="./destructuring.ts" />
/*@internal*/
namespace ts {
@ -404,6 +405,9 @@ namespace ts {
case SyntaxKind.YieldExpression:
return visitYieldExpression(<YieldExpression>node);
case SyntaxKind.SpreadElementExpression:
return visitSpreadElementExpression(<SpreadElementExpression>node);
case SyntaxKind.SuperKeyword:
return visitSuperKeyword();
@ -1129,7 +1133,7 @@ namespace ts {
createVariableStatement(
/*modifiers*/ undefined,
createVariableDeclarationList(
flattenParameterDestructuring(parameter, temp, visitor)
flattenDestructuring(parameter, temp, /*recordTempVariable*/ undefined, visitor)
)
),
EmitFlags.CustomPrologue
@ -1660,18 +1664,21 @@ namespace ts {
*/
function visitParenthesizedExpression(node: ParenthesizedExpression, needsDestructuringValue: boolean): ParenthesizedExpression {
// If we are here it is most likely because our expression is a destructuring assignment.
if (needsDestructuringValue) {
if (!needsDestructuringValue) {
// By default we always emit the RHS at the end of a flattened destructuring
// expression. If we are in a state where we do not need the destructuring value,
// we pass that information along to the children that care about it.
switch (node.expression.kind) {
case SyntaxKind.ParenthesizedExpression:
return createParen(
visitParenthesizedExpression(<ParenthesizedExpression>node.expression, /*needsDestructuringValue*/ true),
/*location*/ node
return updateParen(
node,
visitParenthesizedExpression(<ParenthesizedExpression>node.expression, /*needsDestructuringValue*/ false)
);
case SyntaxKind.BinaryExpression:
return createParen(
visitBinaryExpression(<BinaryExpression>node.expression, /*needsDestructuringValue*/ true),
/*location*/ node
return updateParen(
node,
visitBinaryExpression(<BinaryExpression>node.expression, /*needsDestructuringValue*/ false)
);
}
}
@ -1689,7 +1696,13 @@ namespace ts {
function visitBinaryExpression(node: BinaryExpression, needsDestructuringValue: boolean): Expression {
// If we are here it is because this is a destructuring assignment.
Debug.assert(isDestructuringAssignment(node));
return flattenDestructuringAssignment(context, node, needsDestructuringValue, hoistVariableDeclaration, visitor);
return flattenDestructuringToExpression(
<DestructuringAssignment>node,
needsDestructuringValue,
createAssignment,
hoistVariableDeclaration,
visitor
);
}
function visitVariableStatement(node: VariableStatement): Statement {
@ -1701,15 +1714,17 @@ namespace ts {
if (decl.initializer) {
let assignment: Expression;
if (isBindingPattern(decl.name)) {
assignment = flattenVariableDestructuringToExpression(decl, hoistVariableDeclaration, /*createAssignmentCallback*/ undefined, visitor);
assignment = flattenDestructuringToExpression(decl, /*needsValue*/ false, createAssignment, hoistVariableDeclaration, visitor);
}
else {
assignment = createBinary(<Identifier>decl.name, SyntaxKind.EqualsToken, visitNode(decl.initializer, visitor, isExpression));
}
(assignments || (assignments = [])).push(assignment);
assignments = append(assignments, assignment);
}
}
if (assignments) {
// TODO(rbuckton): use inlineExpressions.
return createStatement(reduceLeft(assignments, (acc, v) => createBinary(v, SyntaxKind.CommaToken, acc)), node);
}
else {
@ -1854,8 +1869,11 @@ namespace ts {
if (isBindingPattern(node.name)) {
const recordTempVariablesInLine = !enclosingVariableStatement
|| !hasModifier(enclosingVariableStatement, ModifierFlags.Export);
return flattenVariableDestructuring(node, /*value*/ undefined, visitor,
recordTempVariablesInLine ? undefined : hoistVariableDeclaration);
return flattenDestructuring(
node,
/*value*/ undefined,
recordTempVariablesInLine ? undefined : hoistVariableDeclaration,
visitor);
}
return visitEachChild(node, visitor, context);
@ -1956,9 +1974,10 @@ namespace ts {
if (firstOriginalDeclaration && isBindingPattern(firstOriginalDeclaration.name)) {
// This works whether the declaration is a var, let, or const.
// It will use rhsIterationValue _a[_i] as the initializer.
const declarations = flattenVariableDestructuring(
const declarations = flattenDestructuring(
firstOriginalDeclaration,
createElementAccess(rhsReference, counter),
/*recordTempVariable*/ undefined,
visitor
);
@ -2007,10 +2026,10 @@ namespace ts {
// This is a destructuring pattern, so we flatten the destructuring instead.
statements.push(
createStatement(
flattenDestructuringAssignment(
context,
flattenDestructuringToExpression(
assignment,
/*needsValue*/ false,
createAssignment,
hoistVariableDeclaration,
visitor
)
@ -2840,7 +2859,7 @@ namespace ts {
}
function visitSpanOfSpreadElements(chunk: Expression[]): VisitResult<Expression> {
return map(chunk, visitExpressionOfSpreadElement);
return map(chunk, visitSpreadElementExpression);
}
function visitSpanOfNonSpreadElements(chunk: Expression[], multiLine: boolean, hasTrailingComma: boolean): VisitResult<Expression> {
@ -2856,7 +2875,7 @@ namespace ts {
*
* @param node A SpreadElementExpression node.
*/
function visitExpressionOfSpreadElement(node: SpreadElementExpression) {
function visitSpreadElementExpression(node: SpreadElementExpression) {
return visitNode(node.expression, visitor, isExpression);
}

View File

@ -1,5 +1,6 @@
/// <reference path="../../factory.ts" />
/// <reference path="../../visitor.ts" />
/// <reference path="../destructuring.ts" />
/*@internal*/
namespace ts {
@ -45,6 +46,7 @@ namespace ts {
let currentSourceFile: SourceFile; // The current file.
let currentModuleInfo: ExternalModuleInfo; // The ExternalModuleInfo for the current file.
let noSubstitution: Map<boolean>; // Set of nodes for which substitution rules should be ignored.
let helperState: EmitHelperState;
return transformSourceFile;
@ -62,6 +64,7 @@ namespace ts {
currentSourceFile = node;
currentModuleInfo = moduleInfoMap[getOriginalNodeId(node)] = collectExternalModuleInfo(node, resolver);
helperState = { currentSourceFile, compilerOptions };
// Perform the transformation.
const transformModule = transformModuleDelegates[moduleKind] || transformModuleDelegates[ModuleKind.None];
@ -69,6 +72,7 @@ namespace ts {
currentSourceFile = undefined;
currentModuleInfo = undefined;
helperState = undefined;
return aggregateTransformFlags(updated);
}
@ -759,10 +763,12 @@ namespace ts {
*/
function transformInitializedVariable(node: VariableDeclaration): Expression {
if (isBindingPattern(node.name)) {
return flattenVariableDestructuringToExpression(
return flattenDestructuringToExpression(
node,
/*needsValue*/ false,
createExportExpression,
hoistVariableDeclaration,
createExportExpression
/*visitor*/ undefined
);
}
else {

View File

@ -1,5 +1,6 @@
/// <reference path="../../factory.ts" />
/// <reference path="../../visitor.ts" />
/// <reference path="../destructuring.ts" />
/*@internal*/
namespace ts {
@ -40,6 +41,7 @@ namespace ts {
let hoistedStatements: Statement[];
let enclosingBlockScopedContainer: Node;
let noSubstitution: Map<boolean>; // Set of nodes for which substitution rules should be ignored.
let helperState: EmitHelperState;
return transformSourceFile;
@ -58,6 +60,7 @@ namespace ts {
const id = getOriginalNodeId(node);
currentSourceFile = node;
enclosingBlockScopedContainer = node;
helperState = { currentSourceFile, compilerOptions };
// System modules have the following shape:
//
@ -131,6 +134,7 @@ namespace ts {
contextObject = undefined;
hoistedStatements = undefined;
enclosingBlockScopedContainer = undefined;
helperState = undefined;
return aggregateTransformFlags(updated);
}
@ -818,7 +822,7 @@ namespace ts {
function transformInitializedVariable(node: VariableDeclaration, isExportedDeclaration: boolean): Expression {
const createAssignment = isExportedDeclaration ? createExportedVariableAssignment : createNonExportedVariableAssignment;
return isBindingPattern(node.name)
? flattenVariableDestructuringToExpression(node, hoistVariableDeclaration, createAssignment, destructuringVisitor)
? flattenDestructuringToExpression(node, /*needsValue*/ false, createAssignment, hoistVariableDeclaration, destructuringVisitor)
: createAssignment(node.name, visitNode(node.initializer, destructuringVisitor, isExpression));
}
@ -1469,9 +1473,8 @@ namespace ts {
*/
function visitDestructuringAssignment(node: DestructuringAssignment): VisitResult<Expression> {
if (hasExportedReferenceInDestructuringTarget(node.left)) {
return flattenDestructuringAssignment(context, node, /*needsValue*/ true, hoistVariableDeclaration, destructuringVisitor);
return flattenDestructuringToExpression(node, /*needsValue*/ true, createAssignment, hoistVariableDeclaration, destructuringVisitor);
}
return visitEachChild(node, destructuringVisitor, context);
}
@ -1481,7 +1484,7 @@ namespace ts {
* @param node The destructuring target.
*/
function hasExportedReferenceInDestructuringTarget(node: Expression | ObjectLiteralElementLike): boolean {
if (isAssignmentExpression(node)) {
if (isAssignmentExpression(node, /*excludeCompoundAssignment*/ true)) {
return hasExportedReferenceInDestructuringTarget(node.left);
}
else if (isSpreadElementExpression(node)) {

View File

@ -2312,12 +2312,7 @@ namespace ts {
function transformInitializedVariable(node: VariableDeclaration): Expression {
const name = node.name;
if (isBindingPattern(name)) {
return flattenVariableDestructuringToExpression(
node,
hoistVariableDeclaration,
createNamespaceExportExpression,
visitor
);
return flattenDestructuringToExpression(node, /*needsValue*/ false, createNamespaceExportExpression, hoistVariableDeclaration, visitor);
}
else {
return createAssignment(

View File

@ -635,9 +635,9 @@ namespace ts {
export interface ParameterDeclaration extends Declaration {
kind: SyntaxKind.Parameter;
dotDotDotToken?: DotDotDotToken; // Present on rest parameter
dotDotDotToken?: DotDotDotToken; // Present on rest parameter
name: BindingName; // Declared parameter name
questionToken?: QuestionToken; // Present on optional parameter
questionToken?: QuestionToken; // Present on optional parameter
type?: TypeNode; // Optional type annotation
initializer?: Expression; // Optional initializer
}
@ -645,7 +645,7 @@ namespace ts {
export interface BindingElement extends Declaration {
kind: SyntaxKind.BindingElement;
propertyName?: PropertyName; // Binding property name (in object binding pattern)
dotDotDotToken?: DotDotDotToken; // Present on rest binding element
dotDotDotToken?: DotDotDotToken; // Present on rest binding element
name: BindingName; // Declared binding element name
initializer?: Expression; // Optional initializer
}
@ -671,7 +671,12 @@ namespace ts {
name?: PropertyName;
}
export type ObjectLiteralElementLike = PropertyAssignment | ShorthandPropertyAssignment | MethodDeclaration | AccessorDeclaration;
export type ObjectLiteralElementLike
= PropertyAssignment
| ShorthandPropertyAssignment
| MethodDeclaration
| AccessorDeclaration
;
export interface PropertyAssignment extends ObjectLiteralElement {
kind: SyntaxKind.PropertyAssignment;
@ -712,6 +717,7 @@ namespace ts {
}
export interface BindingPattern extends Node {
kind: SyntaxKind.ObjectBindingPattern | SyntaxKind.ArrayBindingPattern;
elements: NodeArray<BindingElement | ArrayBindingElement>;
}
@ -1157,16 +1163,16 @@ namespace ts {
right: Expression;
}
export interface AssignmentExpression extends BinaryExpression {
export interface AssignmentExpression<TKind extends AssignmentOperator> extends BinaryExpression {
left: LeftHandSideExpression;
operatorToken: Token<SyntaxKind.EqualsToken>;
operatorToken: Token<TKind>;
}
export interface ObjectDestructuringAssignment extends AssignmentExpression {
export interface ObjectDestructuringAssignment extends AssignmentExpression<SyntaxKind.EqualsToken> {
left: ObjectLiteralExpression;
}
export interface ArrayDestructuringAssignment extends AssignmentExpression {
export interface ArrayDestructuringAssignment extends AssignmentExpression<SyntaxKind.EqualsToken> {
left: ArrayLiteralExpression;
}

View File

@ -3084,19 +3084,21 @@ namespace ts {
}
}
export function isAssignmentExpression(node: Node): node is AssignmentExpression {
export function isAssignmentExpression(node: Node, excludeCompoundAssignment: true): node is AssignmentExpression<SyntaxKind.EqualsToken>;
export function isAssignmentExpression(node: Node, excludeCompoundAssignment?: false): node is AssignmentExpression<AssignmentOperator>;
export function isAssignmentExpression(node: Node, excludeCompoundAssignment?: boolean): node is AssignmentExpression<AssignmentOperator> {
return isBinaryExpression(node)
&& isAssignmentOperator(node.operatorToken.kind)
&& (excludeCompoundAssignment
? node.operatorToken.kind === SyntaxKind.EqualsToken
: isAssignmentOperator(node.operatorToken.kind))
&& isLeftHandSideExpression(node.left);
}
export function isDestructuringAssignment(node: Node): node is DestructuringAssignment {
if (isBinaryExpression(node)) {
if (node.operatorToken.kind === SyntaxKind.EqualsToken) {
const kind = node.left.kind;
return kind === SyntaxKind.ObjectLiteralExpression
|| kind === SyntaxKind.ArrayLiteralExpression;
}
if (isAssignmentExpression(node, /*excludeCompoundAssignment*/ true)) {
const kind = node.left.kind;
return kind === SyntaxKind.ObjectLiteralExpression
|| kind === SyntaxKind.ArrayLiteralExpression;
}
return false;

View File

@ -549,11 +549,11 @@ namespace ts {
export function visitNode<T extends Node>(node: T, visitor: (node: Node) => VisitResult<Node>, test: (node: Node) => boolean, optional?: boolean, lift?: (node: NodeArray<Node>) => T): T;
export function visitNode<T extends Node>(node: T, visitor: (node: Node) => VisitResult<Node>, test: (node: Node) => boolean, optional: boolean, lift: (node: NodeArray<Node>) => T, parenthesize: (node: Node, parentNode: Node) => Node, parentNode: Node): T;
export function visitNode(node: Node, visitor: (node: Node) => VisitResult<Node>, test: (node: Node) => boolean, optional?: boolean, lift?: (node: Node[]) => Node, parenthesize?: (node: Node, parentNode: Node) => Node, parentNode?: Node): Node {
if (node === undefined) {
return undefined;
if (node === undefined || visitor === undefined) {
return node;
}
const visited = visitor(node);
const visited = visitor(aggregateTransformFlags(node));
if (visited === node) {
return node;
}
@ -621,7 +621,7 @@ namespace ts {
// Visit each original node.
for (let i = 0; i < count; i++) {
const node = nodes[i + start];
const visited = node !== undefined ? visitor(node) : undefined;
const visited = node !== undefined ? visitor(aggregateTransformFlags(node)) : undefined;
if (updated !== undefined || visited === undefined || visited !== node) {
if (updated === undefined) {
// Ensure we have a copy of `nodes`, up to the current index.

View File

@ -37,17 +37,17 @@ x = [true][0];
x; // boolean
_a = [1][0], x = _a === void 0 ? "" : _a;
x; // string | number
(_b = { x: true }, x = _b.x, _b);
(x = { x: true }.x);
x; // boolean
(_c = { y: 1 }, x = _c.y, _c);
(x = { y: 1 }.y);
x; // number
(_d = { x: true }, _e = _d.x, x = _e === void 0 ? "" : _e, _d);
(_b = { x: true }.x, x = _b === void 0 ? "" : _b);
x; // string | boolean
(_f = { y: 1 }, _g = _f.y, x = _g === void 0 ? /a/ : _g, _f);
(_c = { y: 1 }.y, x = _c === void 0 ? /a/ : _c);
x; // number | RegExp
var a;
for (var _i = 0, a_1 = a; _i < a_1.length; _i++) {
x = a_1[_i];
x; // string
}
var _a, _b, _c, _d, _e, _f, _g;
var _a, _b, _c;

View File

@ -81,8 +81,8 @@ var B = (function (_super) {
// async method with assignment/destructuring on 'super' requires a binding
B.prototype.advanced = function () {
return __awaiter(this, void 0, void 0, function () {
var f, a, b, _a, _b;
return __generator(this, function (_c) {
var f, a, b;
return __generator(this, function (_a) {
f = function () { };
// call with property access
_super.prototype.x.call(this);
@ -95,9 +95,9 @@ var B = (function (_super) {
// element access (assign)
_super.prototype["x"] = f;
// destructuring assign with property access
(_a = { f: f }, super.x = _a.f, _a);
(super.x = { f: f }.f);
// destructuring assign with element access
(_b = { f: f }, super["x"] = _b.f, _b);
(super["x"] = { f: f }.f);
return [2 /*return*/];
});
});

View File

@ -65,11 +65,11 @@ function f5(_a) {
var _f = foo(), bar6 = [{ bar: "bar" }][0][_f];
var _g = foo.toExponential(), bar7 = [{ bar: "bar" }][0][_g];
// destructuring assignment
(_h = { bar: "bar" }, _j = foo, bar = _h[_j], _h);
(_k = { bar: "bar" }, _l = "bar", bar2 = _k[_l], _k);
(_m = { bar: "bar" }, _o = foo2(), bar3 = _m[_o], _m);
_p = foo, bar4 = [{ bar: "bar" }][0][_p];
_q = foo2(), bar5 = [{ bar: "bar" }][0][_q];
_r = foo(), bar4 = [{ bar: "bar" }][0][_r];
_s = (1 + {}), bar4 = [{ bar: "bar" }][0][_s];
var _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
(_h = foo, bar = { bar: "bar" }[_h]);
(_j = "bar", bar2 = { bar: "bar" }[_j]);
(_k = foo2(), bar3 = { bar: "bar" }[_k]);
_l = foo, bar4 = [{ bar: "bar" }][0][_l];
_m = foo2(), bar5 = [{ bar: "bar" }][0][_m];
_o = foo(), bar4 = [{ bar: "bar" }][0][_o];
_p = (1 + {}), bar4 = [{ bar: "bar" }][0][_p];
var _h, _j, _k, _l, _m, _o, _p;

View File

@ -300,8 +300,8 @@ function f18() {
var a;
var b;
var aa;
(_a = { a: a, b: b }, a = _a.a, b = _a.b, _a);
(_b = { b: b, a: a }, a = _b.a, b = _b.b, _b);
(_a = { a: a, b: b }, a = _a.a, b = _a.b);
(_b = { b: b, a: a }, a = _b.a, b = _b.b);
_c = [a, b], aa[0] = _c[0], b = _c[1];
_d = [b, a], a = _d[0], b = _d[1]; // Error
_e = [2, "def"], _f = _e[0], a = _f === void 0 ? 1 : _f, _g = _e[1], b = _g === void 0 ? "abc" : _g;
@ -311,7 +311,7 @@ function f19() {
var a, b;
_a = [1, 2], a = _a[0], b = _a[1];
_b = [b, a], a = _b[0], b = _b[1];
(_c = { b: b, a: a }, a = _c.a, b = _c.b, _c);
(_c = { b: b, a: a }, a = _c.a, b = _c.b);
_d = [[2, 3]][0], _e = _d === void 0 ? [1, 2] : _d, a = _e[0], b = _e[1];
var x = (_f = [1, 2], a = _f[0], b = _f[1], _f);
var _a, _b, _c, _d, _e, _f;

View File

@ -7,5 +7,5 @@ let x = 0;
//// [destructuringAssignmentWithDefault.js]
var a = {};
var x = 0;
(_a = a.x, x = _a === void 0 ? 1 : _a, a);
(_a = a.x, x = _a === void 0 ? 1 : _a);
var _a;

View File

@ -9,8 +9,8 @@ let x, y, z, a1, a2, a3;
//// [emptyAssignmentPatterns02_ES5.js]
var a;
var x, y, z, a1, a2, a3;
(x = a.x, y = a.y, z = a.z, a);
(a1 = a[0], a2 = a[1], a3 = a[2], a);
(x = a.x, y = a.y, z = a.z);
(a1 = a[0], a2 = a[1], a3 = a[2]);
//// [emptyAssignmentPatterns02_ES5.d.ts]

View File

@ -9,9 +9,8 @@ let x, y, z, a1, a2, a3;
//// [emptyAssignmentPatterns04_ES5.js]
var a;
var x, y, z, a1, a2, a3;
(_a = a, x = _a.x, y = _a.y, z = _a.z, _a);
(_b = a, a1 = _b[0], a2 = _b[1], a3 = _b[2], _b);
var _a, _b;
(x = a.x, y = a.y, z = a.z);
(a1 = a[0], a2 = a[1], a3 = a[2]);
//// [emptyAssignmentPatterns04_ES5.d.ts]

View File

@ -28,7 +28,6 @@ if (true) {
var x_1 = { x: 0 }.x;
var y_1 = { y: 0 }.y;
var z_1;
(_a = { z: 0 }, z_1 = _a.z, _a);
(_b = { z: 0 }, z_1 = _b.z, _b);
(z_1 = { z: 0 }.z);
(z_1 = { z: 0 }.z);
}
var _a, _b;

View File

@ -45,10 +45,10 @@ function f1() {
// Missing properties
function f2() {
var x, y;
(_a = {}, x = _a.x, y = _a.y, _a);
(_b = {}, _c = _b.x, x = _c === void 0 ? 1 : _c, y = _b.y, _b);
(_d = {}, x = _d.x, _e = _d.y, y = _e === void 0 ? 1 : _e, _d);
(_f = {}, _g = _f.x, x = _g === void 0 ? 1 : _g, _h = _f.y, y = _h === void 0 ? 1 : _h, _f);
(_a = {}, x = _a.x, y = _a.y);
(_b = {}, _c = _b.x, x = _c === void 0 ? 1 : _c, y = _b.y);
(_d = {}, x = _d.x, _e = _d.y, y = _e === void 0 ? 1 : _e);
(_f = {}, _g = _f.x, x = _g === void 0 ? 1 : _g, _h = _f.y, y = _h === void 0 ? 1 : _h);
var _a, _b, _c, _d, _e, _f, _g, _h;
}
// Excess properties
@ -62,8 +62,8 @@ function f3() {
function f4() {
var x, y;
({ x: 0, y: 0 });
(_a = { x: 0, y: 0 }, x = _a.x, _a);
(_b = { x: 0, y: 0 }, y = _b.y, _b);
(_c = { x: 0, y: 0 }, x = _c.x, y = _c.y, _c);
var _a, _b, _c;
(x = { x: 0, y: 0 }.x);
(y = { x: 0, y: 0 }.y);
(_a = { x: 0, y: 0 }, x = _a.x, y = _a.y);
var _a;
}

View File

@ -176,63 +176,63 @@ function foo({a = 4, b = { x: 5 }}) {
});
(function () {
var y;
(_a = { y: 1 }, _b = _a.y, y = _b === void 0 ? 5 : _b, _a);
var _a, _b;
(_a = { y: 1 }.y, y = _a === void 0 ? 5 : _a);
var _a;
});
(function () {
var y;
(_a = { y: 1 }, _b = _a.y, y = _b === void 0 ? 5 : _b, _a);
var _a, _b;
(_a = { y: 1 }.y, y = _a === void 0 ? 5 : _a);
var _a;
});
(function () {
var y0;
(_a = { y0: 1 }, _b = _a.y0, y0 = _b === void 0 ? 5 : _b, _a);
var _a, _b;
(_a = { y0: 1 }.y0, y0 = _a === void 0 ? 5 : _a);
var _a;
});
(function () {
var y0;
(_a = { y0: 1 }, _b = _a.y0, y0 = _b === void 0 ? 5 : _b, _a);
var _a, _b;
(_a = { y0: 1 }.y0, y0 = _a === void 0 ? 5 : _a);
var _a;
});
(function () {
var y1;
(_a = {}, _b = _a.y1, y1 = _b === void 0 ? 5 : _b, _a);
var _a, _b;
(_a = {}.y1, y1 = _a === void 0 ? 5 : _a);
var _a;
});
(function () {
var y1;
(_a = {}, _b = _a.y1, y1 = _b === void 0 ? 5 : _b, _a);
var _a, _b;
(_a = {}.y1, y1 = _a === void 0 ? 5 : _a);
var _a;
});
(function () {
var y2, y3;
(_a = {}, _b = _a.y2, y2 = _b === void 0 ? 5 : _b, _c = _a.y3, y3 = _c === void 0 ? { x: 1 } : _c, _a);
(_a = {}, _b = _a.y2, y2 = _b === void 0 ? 5 : _b, _c = _a.y3, y3 = _c === void 0 ? { x: 1 } : _c);
var _a, _b, _c;
});
(function () {
var y2, y3;
(_a = {}, _b = _a.y2, y2 = _b === void 0 ? 5 : _b, _c = _a.y3, y3 = _c === void 0 ? { x: 1 } : _c, _a);
(_a = {}, _b = _a.y2, y2 = _b === void 0 ? 5 : _b, _c = _a.y3, y3 = _c === void 0 ? { x: 1 } : _c);
var _a, _b, _c;
});
(function () {
var y4, y5;
(_a = {}, _b = _a.y4, y4 = _b === void 0 ? 5 : _b, _c = _a.y5, y5 = _c === void 0 ? { x: 1 } : _c, _a);
(_a = {}, _b = _a.y4, y4 = _b === void 0 ? 5 : _b, _c = _a.y5, y5 = _c === void 0 ? { x: 1 } : _c);
var _a, _b, _c;
});
(function () {
var y4, y5;
(_a = {}, _b = _a.y4, y4 = _b === void 0 ? 5 : _b, _c = _a.y5, y5 = _c === void 0 ? { x: 1 } : _c, _a);
(_a = {}, _b = _a.y4, y4 = _b === void 0 ? 5 : _b, _c = _a.y5, y5 = _c === void 0 ? { x: 1 } : _c);
var _a, _b, _c;
});
(function () {
var z;
(_a = { z: { x: 1 } }, _b = _a.z, z = _b === void 0 ? { x: 5 } : _b, _a);
var _a, _b;
(_a = { z: { x: 1 } }.z, z = _a === void 0 ? { x: 5 } : _a);
var _a;
});
(function () {
var z;
(_a = { z: { x: 1 } }, _b = _a.z, z = _b === void 0 ? { x: 5 } : _b, _a);
var _a, _b;
(_a = { z: { x: 1 } }.z, z = _a === void 0 ? { x: 5 } : _a);
var _a;
});
(function () {
var a = { s: s };