Skip to content

Commit cc342d1

Browse files
committed
Move convertForOf to factory for esnext and es2015
Saves a lot of duplicated code
1 parent 71f3157 commit cc342d1

File tree

3 files changed

+192
-251
lines changed

3 files changed

+192
-251
lines changed

src/compiler/factory.ts

+190
Original file line numberDiff line numberDiff line change
@@ -3055,4 +3055,194 @@ namespace ts {
30553055
function tryGetModuleNameFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration, host: EmitHost, resolver: EmitResolver, compilerOptions: CompilerOptions) {
30563056
return tryGetModuleNameFromFile(resolver.getExternalModuleFileFromDeclaration(declaration), host, compilerOptions);
30573057
}
3058+
3059+
export function convertForOf(node: ForOfStatement, convertedLoopBodyStatements: Statement[],
3060+
visitor: (node: Node) => VisitResult<Node>,
3061+
enableSubstitutionsForBlockScopedBindings: () => void,
3062+
context: TransformationContext,
3063+
convertObjectRest?: boolean): ForStatement | ForOfStatement {
3064+
// The following ES6 code:
3065+
//
3066+
// for (let v of expr) { }
3067+
//
3068+
// should be emitted as
3069+
//
3070+
// for (var _i = 0, _a = expr; _i < _a.length; _i++) {
3071+
// var v = _a[_i];
3072+
// }
3073+
//
3074+
// where _a and _i are temps emitted to capture the RHS and the counter,
3075+
// respectively.
3076+
// When the left hand side is an expression instead of a let declaration,
3077+
// the "let v" is not emitted.
3078+
// When the left hand side is a let/const, the v is renamed if there is
3079+
// another v in scope.
3080+
// Note that all assignments to the LHS are emitted in the body, including
3081+
// all destructuring.
3082+
// Note also that because an extra statement is needed to assign to the LHS,
3083+
// for-of bodies are always emitted as blocks.
3084+
3085+
const expression = visitNode(node.expression, visitor, isExpression);
3086+
const initializer = node.initializer;
3087+
const statements: Statement[] = [];
3088+
3089+
// In the case where the user wrote an identifier as the RHS, like this:
3090+
//
3091+
// for (let v of arr) { }
3092+
//
3093+
// we don't want to emit a temporary variable for the RHS, just use it directly.
3094+
const counter = convertObjectRest ? undefined : createLoopVariable();
3095+
const rhsReference = expression.kind === SyntaxKind.Identifier
3096+
? createUniqueName((<Identifier>expression).text)
3097+
: createTempVariable(/*recordTempVariable*/ undefined);
3098+
const elementAccess = convertObjectRest ? rhsReference : createElementAccess(rhsReference, counter);
3099+
3100+
// Initialize LHS
3101+
// var v = _a[_i];
3102+
if (isVariableDeclarationList(initializer)) {
3103+
if (initializer.flags & NodeFlags.BlockScoped) {
3104+
enableSubstitutionsForBlockScopedBindings();
3105+
}
3106+
3107+
const firstOriginalDeclaration = firstOrUndefined(initializer.declarations);
3108+
if (firstOriginalDeclaration && isBindingPattern(firstOriginalDeclaration.name)) {
3109+
// This works whether the declaration is a var, let, or const.
3110+
// It will use rhsIterationValue _a[_i] as the initializer.
3111+
const declarations = flattenVariableDestructuring(
3112+
firstOriginalDeclaration,
3113+
elementAccess,
3114+
visitor,
3115+
/*recordTempVariable*/ undefined,
3116+
convertObjectRest
3117+
);
3118+
3119+
const declarationList = createVariableDeclarationList(declarations, /*location*/ initializer);
3120+
setOriginalNode(declarationList, initializer);
3121+
3122+
// Adjust the source map range for the first declaration to align with the old
3123+
// emitter.
3124+
const firstDeclaration = declarations[0];
3125+
const lastDeclaration = lastOrUndefined(declarations);
3126+
setSourceMapRange(declarationList, createRange(firstDeclaration.pos, lastDeclaration.end));
3127+
3128+
statements.push(
3129+
createVariableStatement(
3130+
/*modifiers*/ undefined,
3131+
declarationList
3132+
)
3133+
);
3134+
}
3135+
else {
3136+
// The following call does not include the initializer, so we have
3137+
// to emit it separately.
3138+
statements.push(
3139+
createVariableStatement(
3140+
/*modifiers*/ undefined,
3141+
setOriginalNode(
3142+
createVariableDeclarationList([
3143+
createVariableDeclaration(
3144+
firstOriginalDeclaration ? firstOriginalDeclaration.name : createTempVariable(/*recordTempVariable*/ undefined),
3145+
/*type*/ undefined,
3146+
createElementAccess(rhsReference, counter)
3147+
)
3148+
], /*location*/ moveRangePos(initializer, -1)),
3149+
initializer
3150+
),
3151+
/*location*/ moveRangeEnd(initializer, -1)
3152+
)
3153+
);
3154+
}
3155+
}
3156+
else {
3157+
// Initializer is an expression. Emit the expression in the body, so that it's
3158+
// evaluated on every iteration.
3159+
const assignment = createAssignment(initializer, elementAccess);
3160+
if (isDestructuringAssignment(assignment)) {
3161+
// This is a destructuring pattern, so we flatten the destructuring instead.
3162+
statements.push(
3163+
createStatement(
3164+
flattenDestructuringAssignment(
3165+
context,
3166+
assignment,
3167+
/*needsValue*/ false,
3168+
context.hoistVariableDeclaration,
3169+
visitor,
3170+
convertObjectRest
3171+
)
3172+
)
3173+
);
3174+
}
3175+
else {
3176+
// Currently there is not way to check that assignment is binary expression of destructing assignment
3177+
// so we have to cast never type to binaryExpression
3178+
(<BinaryExpression>assignment).end = initializer.end;
3179+
statements.push(createStatement(assignment, /*location*/ moveRangeEnd(initializer, -1)));
3180+
}
3181+
}
3182+
3183+
let bodyLocation: TextRange;
3184+
let statementsLocation: TextRange;
3185+
if (convertedLoopBodyStatements) {
3186+
addRange(statements, convertedLoopBodyStatements);
3187+
}
3188+
else {
3189+
const statement = visitNode(node.statement, visitor, isStatement);
3190+
if (isBlock(statement)) {
3191+
addRange(statements, statement.statements);
3192+
bodyLocation = statement;
3193+
statementsLocation = statement.statements;
3194+
}
3195+
else {
3196+
statements.push(statement);
3197+
}
3198+
}
3199+
3200+
// The old emitter does not emit source maps for the expression
3201+
setEmitFlags(expression, EmitFlags.NoSourceMap | getEmitFlags(expression));
3202+
3203+
// The old emitter does not emit source maps for the block.
3204+
// We add the location to preserve comments.
3205+
const body = createBlock(
3206+
createNodeArray(statements, /*location*/ statementsLocation),
3207+
/*location*/ bodyLocation
3208+
);
3209+
3210+
setEmitFlags(body, EmitFlags.NoSourceMap | EmitFlags.NoTokenSourceMaps);
3211+
3212+
let forStatement: ForStatement | ForOfStatement;
3213+
if(convertObjectRest) {
3214+
3215+
forStatement = createForOf(
3216+
createVariableDeclarationList([
3217+
createVariableDeclaration(rhsReference, /*type*/ undefined, /*initializer*/ undefined, /*location*/ node.expression)
3218+
], /*location*/ node.expression),
3219+
node.expression,
3220+
body,
3221+
/*location*/ node
3222+
);
3223+
}
3224+
else {
3225+
forStatement = createFor(
3226+
setEmitFlags(
3227+
createVariableDeclarationList([
3228+
createVariableDeclaration(counter, /*type*/ undefined, createLiteral(0), /*location*/ moveRangePos(node.expression, -1)),
3229+
createVariableDeclaration(rhsReference, /*type*/ undefined, expression, /*location*/ node.expression)
3230+
], /*location*/ node.expression),
3231+
EmitFlags.NoHoisting
3232+
),
3233+
createLessThan(
3234+
counter,
3235+
createPropertyAccess(rhsReference, "length"),
3236+
/*location*/ node.expression
3237+
),
3238+
createPostfixIncrement(counter, /*location*/ node.expression),
3239+
body,
3240+
/*location*/ node
3241+
);
3242+
}
3243+
3244+
// Disable trailing source maps for the OpenParenToken to align source map emit with the old emitter.
3245+
setEmitFlags(forStatement, EmitFlags.NoTokenTrailingSourceMaps);
3246+
return forStatement;
3247+
}
30583248
}

src/compiler/transformers/es2015.ts

+1-165
Original file line numberDiff line numberDiff line change
@@ -1926,171 +1926,7 @@ namespace ts {
19261926
}
19271927

19281928
function convertForOfToFor(node: ForOfStatement, convertedLoopBodyStatements: Statement[]): ForStatement {
1929-
// The following ES6 code:
1930-
//
1931-
// for (let v of expr) { }
1932-
//
1933-
// should be emitted as
1934-
//
1935-
// for (var _i = 0, _a = expr; _i < _a.length; _i++) {
1936-
// var v = _a[_i];
1937-
// }
1938-
//
1939-
// where _a and _i are temps emitted to capture the RHS and the counter,
1940-
// respectively.
1941-
// When the left hand side is an expression instead of a let declaration,
1942-
// the "let v" is not emitted.
1943-
// When the left hand side is a let/const, the v is renamed if there is
1944-
// another v in scope.
1945-
// Note that all assignments to the LHS are emitted in the body, including
1946-
// all destructuring.
1947-
// Note also that because an extra statement is needed to assign to the LHS,
1948-
// for-of bodies are always emitted as blocks.
1949-
1950-
const expression = visitNode(node.expression, visitor, isExpression);
1951-
const initializer = node.initializer;
1952-
const statements: Statement[] = [];
1953-
1954-
// In the case where the user wrote an identifier as the RHS, like this:
1955-
//
1956-
// for (let v of arr) { }
1957-
//
1958-
// we don't want to emit a temporary variable for the RHS, just use it directly.
1959-
const counter = createLoopVariable();
1960-
const rhsReference = expression.kind === SyntaxKind.Identifier
1961-
? createUniqueName((<Identifier>expression).text)
1962-
: createTempVariable(/*recordTempVariable*/ undefined);
1963-
1964-
// Initialize LHS
1965-
// var v = _a[_i];
1966-
if (isVariableDeclarationList(initializer)) {
1967-
if (initializer.flags & NodeFlags.BlockScoped) {
1968-
enableSubstitutionsForBlockScopedBindings();
1969-
}
1970-
1971-
const firstOriginalDeclaration = firstOrUndefined(initializer.declarations);
1972-
if (firstOriginalDeclaration && isBindingPattern(firstOriginalDeclaration.name)) {
1973-
// This works whether the declaration is a var, let, or const.
1974-
// It will use rhsIterationValue _a[_i] as the initializer.
1975-
const declarations = flattenVariableDestructuring(
1976-
firstOriginalDeclaration,
1977-
createElementAccess(rhsReference, counter),
1978-
visitor
1979-
);
1980-
1981-
const declarationList = createVariableDeclarationList(declarations, /*location*/ initializer);
1982-
setOriginalNode(declarationList, initializer);
1983-
1984-
// Adjust the source map range for the first declaration to align with the old
1985-
// emitter.
1986-
const firstDeclaration = declarations[0];
1987-
const lastDeclaration = lastOrUndefined(declarations);
1988-
setSourceMapRange(declarationList, createRange(firstDeclaration.pos, lastDeclaration.end));
1989-
1990-
statements.push(
1991-
createVariableStatement(
1992-
/*modifiers*/ undefined,
1993-
declarationList
1994-
)
1995-
);
1996-
}
1997-
else {
1998-
// The following call does not include the initializer, so we have
1999-
// to emit it separately.
2000-
statements.push(
2001-
createVariableStatement(
2002-
/*modifiers*/ undefined,
2003-
setOriginalNode(
2004-
createVariableDeclarationList([
2005-
createVariableDeclaration(
2006-
firstOriginalDeclaration ? firstOriginalDeclaration.name : createTempVariable(/*recordTempVariable*/ undefined),
2007-
/*type*/ undefined,
2008-
createElementAccess(rhsReference, counter)
2009-
)
2010-
], /*location*/ moveRangePos(initializer, -1)),
2011-
initializer
2012-
),
2013-
/*location*/ moveRangeEnd(initializer, -1)
2014-
)
2015-
);
2016-
}
2017-
}
2018-
else {
2019-
// Initializer is an expression. Emit the expression in the body, so that it's
2020-
// evaluated on every iteration.
2021-
const assignment = createAssignment(initializer, createElementAccess(rhsReference, counter));
2022-
if (isDestructuringAssignment(assignment)) {
2023-
// This is a destructuring pattern, so we flatten the destructuring instead.
2024-
statements.push(
2025-
createStatement(
2026-
flattenDestructuringAssignment(
2027-
context,
2028-
assignment,
2029-
/*needsValue*/ false,
2030-
hoistVariableDeclaration,
2031-
visitor
2032-
)
2033-
)
2034-
);
2035-
}
2036-
else {
2037-
// Currently there is not way to check that assignment is binary expression of destructing assignment
2038-
// so we have to cast never type to binaryExpression
2039-
(<BinaryExpression>assignment).end = initializer.end;
2040-
statements.push(createStatement(assignment, /*location*/ moveRangeEnd(initializer, -1)));
2041-
}
2042-
}
2043-
2044-
let bodyLocation: TextRange;
2045-
let statementsLocation: TextRange;
2046-
if (convertedLoopBodyStatements) {
2047-
addRange(statements, convertedLoopBodyStatements);
2048-
}
2049-
else {
2050-
const statement = visitNode(node.statement, visitor, isStatement);
2051-
if (isBlock(statement)) {
2052-
addRange(statements, statement.statements);
2053-
bodyLocation = statement;
2054-
statementsLocation = statement.statements;
2055-
}
2056-
else {
2057-
statements.push(statement);
2058-
}
2059-
}
2060-
2061-
// The old emitter does not emit source maps for the expression
2062-
setEmitFlags(expression, EmitFlags.NoSourceMap | getEmitFlags(expression));
2063-
2064-
// The old emitter does not emit source maps for the block.
2065-
// We add the location to preserve comments.
2066-
const body = createBlock(
2067-
createNodeArray(statements, /*location*/ statementsLocation),
2068-
/*location*/ bodyLocation
2069-
);
2070-
2071-
setEmitFlags(body, EmitFlags.NoSourceMap | EmitFlags.NoTokenSourceMaps);
2072-
2073-
const forStatement = createFor(
2074-
setEmitFlags(
2075-
createVariableDeclarationList([
2076-
createVariableDeclaration(counter, /*type*/ undefined, createLiteral(0), /*location*/ moveRangePos(node.expression, -1)),
2077-
createVariableDeclaration(rhsReference, /*type*/ undefined, expression, /*location*/ node.expression)
2078-
], /*location*/ node.expression),
2079-
EmitFlags.NoHoisting
2080-
),
2081-
createLessThan(
2082-
counter,
2083-
createPropertyAccess(rhsReference, "length"),
2084-
/*location*/ node.expression
2085-
),
2086-
createPostfixIncrement(counter, /*location*/ node.expression),
2087-
body,
2088-
/*location*/ node
2089-
);
2090-
2091-
// Disable trailing source maps for the OpenParenToken to align source map emit with the old emitter.
2092-
setEmitFlags(forStatement, EmitFlags.NoTokenTrailingSourceMaps);
2093-
return forStatement;
1929+
return <ForStatement>convertForOf(node, convertedLoopBodyStatements, visitor, enableSubstitutionsForBlockScopedBindings, context, /*transformRest*/ false);
20941930
}
20951931

20961932
/**

0 commit comments

Comments
 (0)