Move emit helpers into related transformers

This commit is contained in:
Ron Buckton
2016-10-21 18:44:13 -07:00
parent e6bea90a1f
commit 83f5d3f25c
14 changed files with 1104 additions and 1107 deletions

View File

@@ -19034,7 +19034,7 @@ namespace ts {
function isNameOfModuleOrEnumDeclaration(node: Identifier) {
const parent = node.parent;
return isModuleOrEnumDeclaration(parent) && node === parent.name;
return parent && isModuleOrEnumDeclaration(parent) && node === parent.name;
}
// When resolved as an expression identifier, if the given node references an exported entity, return the declaration

View File

@@ -538,7 +538,7 @@ namespace ts {
*/
export function append<T>(to: T[] | undefined, value: T | undefined): T[] | undefined {
if (value === undefined) return to;
if (to === undefined) to = [];
if (to === undefined) return [value];
to.push(value);
return to;
}
@@ -559,6 +559,16 @@ namespace ts {
return to;
}
/**
* Stable sort of an array. Elements equal to each other maintain their relative position in the array.
*/
export function stableSort<T>(array: T[], comparer: (x: T, y: T) => Comparison = compareValues) {
return array
.map((_, i) => i) // create array of indices
.sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y)) // sort indices by value then position
.map(i => array[i]); // get sorted array
}
export function rangeEquals<T>(array1: T[], array2: T[], pos: number, end: number) {
while (pos < end) {
if (array1[pos] !== array2[pos]) {
@@ -2022,6 +2032,17 @@ namespace ts {
}
/** Remove an item from an array, moving everything to its right one space left. */
export function orderedRemoveItem<T>(array: T[], item: T): boolean {
for (let i = 0; i < array.length; i++) {
if (array[i] === item) {
orderedRemoveItemAt(array, i);
return true;
}
}
return false;
}
/** Remove an item by index from an array, moving everything to its right one space left. */
export function orderedRemoveItemAt<T>(array: T[], index: number): void {
// This seems to be faster than either `array.splice(i, 1)` or `array.copyWithin(i, i+ 1)`.
for (let i = index; i < array.length - 1; i++) {

View File

@@ -21,153 +21,6 @@ namespace ts {
const delimiters = createDelimiterMap();
const brackets = createBracketsMap();
// emit output for the __extends helper function
const extendsHelper = `
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};`;
// Emit output for the __assign helper function.
// This is typically used for JSX spread attributes,
// and can be used for object literal spread properties.
const assignHelper = `
var __assign = (this && this.__assign) || Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};`;
// emit output for the __decorate helper function
const decorateHelper = `
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};`;
// emit output for the __metadata helper function
const metadataHelper = `
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};`;
// emit output for the __param helper function
const paramHelper = `
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};`;
// emit output for the __awaiter helper function
const awaiterHelper = `
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments)).next());
});
};`;
// The __generator helper is used by down-level transformations to emulate the runtime
// semantics of an ES2015 generator function. When called, this helper returns an
// object that implements the Iterator protocol, in that it has `next`, `return`, and
// `throw` methods that step through the generator when invoked.
//
// parameters:
// thisArg The value to use as the `this` binding for the transformed generator body.
// body A function that acts as the transformed generator body.
//
// variables:
// _ Persistent state for the generator that is shared between the helper and the
// generator body. The state object has the following members:
// sent() - A method that returns or throws the current completion value.
// label - The next point at which to resume evaluation of the generator body.
// trys - A stack of protected regions (try/catch/finally blocks).
// ops - A stack of pending instructions when inside of a finally block.
// f A value indicating whether the generator is executing.
// y An iterator to delegate for a yield*.
// t A temporary variable that holds one of the following values (note that these
// cases do not overlap):
// - The completion value when resuming from a `yield` or `yield*`.
// - The error value for a catch block.
// - The current protected region (array of try/catch/finally/end labels).
// - The verb (`next`, `throw`, or `return` method) to delegate to the expression
// of a `yield*`.
// - The result of evaluating the verb delegated to the expression of a `yield*`.
//
// functions:
// verb(n) Creates a bound callback to the `step` function for opcode `n`.
// step(op) Evaluates opcodes in a generator body until execution is suspended or
// completed.
//
// The __generator helper understands a limited set of instructions:
// 0: next(value?) - Start or resume the generator with the specified value.
// 1: throw(error) - Resume the generator with an exception. If the generator is
// suspended inside of one or more protected regions, evaluates
// any intervening finally blocks between the current label and
// the nearest catch block or function boundary. If uncaught, the
// exception is thrown to the caller.
// 2: return(value?) - Resume the generator as if with a return. If the generator is
// suspended inside of one or more protected regions, evaluates any
// intervening finally blocks.
// 3: break(label) - Jump to the specified label. If the label is outside of the
// current protected region, evaluates any intervening finally
// blocks.
// 4: yield(value?) - Yield execution to the caller with an optional value. When
// resumed, the generator will continue at the next label.
// 5: yield*(value) - Delegates evaluation to the supplied iterator. When
// delegation completes, the generator will continue at the next
// label.
// 6: catch(error) - Handles an exception thrown from within the generator body. If
// the current label is inside of one or more protected regions,
// evaluates any intervening finally blocks between the current
// label and the nearest catch block or function boundary. If
// uncaught, the exception is thrown to the caller.
// 7: endfinally - Ends a finally block, resuming the last instruction prior to
// entering a finally block.
//
// For examples of how these are used, see the comments in ./transformers/generators.ts
const generatorHelper = `
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t;
return { next: verb(0), "throw": verb(1), "return": verb(2) };
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [0, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};`;
// emit output for the __export helper function
const exportStarHelper = `
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}`;
// emit output for the UMD helper function.
const umdHelper = `
(function (dependencies, factory) {
@@ -179,15 +32,6 @@ function __export(m) {
}
})`;
const superHelper = `
const _super = name => super[name];`;
const advancedSuperHelper = `
const _super = (function (geti, seti) {
const cache = Object.create(null);
return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
})(name => super[name], (name, value) => super[name] = value);`;
const compilerOptions = host.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);
const moduleKind = getEmitModuleKind(compilerOptions);
@@ -224,11 +68,7 @@ const _super = (function (geti, seti) {
let currentSourceFile: SourceFile;
let currentText: string;
let currentFileIdentifiers: Map<string>;
let extendsEmitted: boolean;
let assignEmitted: boolean;
let decorateEmitted: boolean;
let paramEmitted: boolean;
let awaiterEmitted: boolean;
let bundledHelpers: Map<boolean>;
let isOwnFileEmit: boolean;
let emitSkipped = false;
@@ -293,12 +133,13 @@ const _super = (function (geti, seti) {
nodeIdToGeneratedName = [];
autoGeneratedIdToGeneratedName = [];
generatedNameSet = createMap<string>();
bundledHelpers = isBundledEmit ? createMap<boolean>() : undefined;
isOwnFileEmit = !isBundledEmit;
// Emit helpers from all the files
if (isBundledEmit && moduleKind) {
for (const sourceFile of sourceFiles) {
emitEmitHelpers(sourceFile);
emitHelpers(sourceFile, /*isBundle*/ true);
}
}
@@ -333,11 +174,6 @@ const _super = (function (geti, seti) {
tempFlags = TempFlags.Auto;
currentSourceFile = undefined;
currentText = undefined;
extendsEmitted = false;
assignEmitted = false;
decorateEmitted = false;
paramEmitted = false;
awaiterEmitted = false;
isOwnFileEmit = false;
}
@@ -2141,85 +1977,39 @@ const _super = (function (geti, seti) {
return statements.length;
}
function emitHelpers(node: Node) {
const emitFlags = getEmitFlags(node);
let helpersEmitted = false;
if (emitFlags & EmitFlags.EmitEmitHelpers) {
helpersEmitted = emitEmitHelpers(currentSourceFile);
}
if (emitFlags & EmitFlags.EmitExportStar) {
writeLines(exportStarHelper);
helpersEmitted = true;
}
if (emitFlags & EmitFlags.EmitSuperHelper) {
writeLines(superHelper);
helpersEmitted = true;
}
if (emitFlags & EmitFlags.EmitAdvancedSuperHelper) {
writeLines(advancedSuperHelper);
helpersEmitted = true;
}
return helpersEmitted;
}
function emitEmitHelpers(node: SourceFile) {
// Only emit helpers if the user did not say otherwise.
if (compilerOptions.noEmitHelpers) {
return false;
}
// Don't emit helpers if we can import them.
if (compilerOptions.importHelpers
&& (isExternalModule(node) || compilerOptions.isolatedModules)) {
return false;
}
function emitHelpers(node: Node, isBundle?: boolean) {
const sourceFile = isSourceFile(node) ? node : currentSourceFile;
const shouldSkip = compilerOptions.noEmitHelpers || (sourceFile && getExternalHelpersModuleName(sourceFile) !== undefined);
const shouldBundle = isSourceFile(node) && !isOwnFileEmit;
let helpersEmitted = false;
const helpers = getEmitHelpers(node);
if (helpers) {
for (const helper of stableSort(helpers, compareEmitHelpers)) {
if (!helper.scoped) {
// Skip the helper if it can be skipped and the noEmitHelpers compiler
// option is set, or if it can be imported and the importHelpers compiler
// option is set.
if (shouldSkip) continue;
// Only Emit __extends function when target ES5.
// For target ES6 and above, we can emit classDeclaration as is.
if ((languageVersion < ScriptTarget.ES2015) && (!extendsEmitted && node.flags & NodeFlags.HasClassExtends)) {
writeLines(extendsHelper);
extendsEmitted = true;
helpersEmitted = true;
}
// Skip the helper if it can be bundled but hasn't already been emitted and we
// are emitting a bundled module.
if (shouldBundle) {
if (helper.name in bundledHelpers) {
continue;
}
if (compilerOptions.jsx !== JsxEmit.Preserve && !assignEmitted && (node.flags & NodeFlags.HasJsxSpreadAttributes)) {
writeLines(assignHelper);
assignEmitted = true;
}
bundledHelpers[helper.name] = true;
}
}
else if (isBundle) {
// Skip the helper if it is scoped and we are emitting bundled helpers
continue;
}
if (!decorateEmitted && node.flags & NodeFlags.HasDecorators) {
writeLines(decorateHelper);
if (compilerOptions.emitDecoratorMetadata) {
writeLines(metadataHelper);
writeLines(helper.text);
helpersEmitted = true;
}
decorateEmitted = true;
helpersEmitted = true;
}
if (!paramEmitted && node.flags & NodeFlags.HasParamDecorators) {
writeLines(paramHelper);
paramEmitted = true;
helpersEmitted = true;
}
// Only emit __awaiter function when target ES5/ES6.
// Only emit __generator function when target ES5.
// For target ES2017 and above, we can emit async/await as is.
if ((languageVersion < ScriptTarget.ES2017) && (!awaiterEmitted && node.flags & NodeFlags.HasAsyncFunctions)) {
writeLines(awaiterHelper);
if (languageVersion < ScriptTarget.ES2015) {
writeLines(generatorHelper);
}
awaiterEmitted = true;
helpersEmitted = true;
}
if (helpersEmitted) {
@@ -2230,9 +2020,10 @@ const _super = (function (geti, seti) {
}
function writeLines(text: string): void {
const lines = text.split(/\r\n|\r|\n/g);
const lines = text.split(/\r\n?|\n/g);
const indentation = guessIndentation(lines);
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const line = indentation ? lines[i].slice(indentation) : lines[i];
if (line.length) {
if (i > 0) {
writeLine();
@@ -2242,6 +2033,21 @@ const _super = (function (geti, seti) {
}
}
function guessIndentation(lines: string[]) {
let indentation: number;
for (const line of lines) {
for (let i = 0; i < line.length && (indentation === undefined || i < indentation); i++) {
if (!isWhiteSpace(line.charCodeAt(i))) {
if (indentation === undefined || i < indentation) {
indentation = i;
break;
}
}
}
}
return indentation;
}
//
// Helpers
//

View File

@@ -1440,7 +1440,6 @@ namespace ts {
if (node.resolvedTypeReferenceDirectiveNames !== undefined) updated.resolvedTypeReferenceDirectiveNames = node.resolvedTypeReferenceDirectiveNames;
if (node.imports !== undefined) updated.imports = node.imports;
if (node.moduleAugmentations !== undefined) updated.moduleAugmentations = node.moduleAugmentations;
if (node.externalHelpersModuleName !== undefined) updated.externalHelpersModuleName = node.externalHelpersModuleName;
return updateNode(updated, node);
}
@@ -1683,278 +1682,23 @@ namespace ts {
// Helpers
export function createHelperName(externalHelpersModuleName: Identifier | undefined, name: string) {
export interface EmitHelperState {
currentSourceFile: SourceFile;
compilerOptions: CompilerOptions;
requestedHelpers?: EmitHelper[];
}
export function getHelperName(helperState: EmitHelperState, name: string) {
const externalHelpersModuleName = getOrCreateExternalHelpersModuleName(helperState.currentSourceFile, helperState.compilerOptions);
return externalHelpersModuleName
? createPropertyAccess(externalHelpersModuleName, name)
: createIdentifier(name);
}
export function createExtendsHelper(externalHelpersModuleName: Identifier | undefined, name: Identifier) {
return createCall(
createHelperName(externalHelpersModuleName, "__extends"),
/*typeArguments*/ undefined,
[
name,
createIdentifier("_super")
]
);
}
export function createAssignHelper(externalHelpersModuleName: Identifier | undefined, attributesSegments: Expression[]) {
return createCall(
createHelperName(externalHelpersModuleName, "__assign"),
/*typeArguments*/ undefined,
attributesSegments
);
}
export function createParamHelper(externalHelpersModuleName: Identifier | undefined, expression: Expression, parameterOffset: number, location?: TextRange) {
return createCall(
createHelperName(externalHelpersModuleName, "__param"),
/*typeArguments*/ undefined,
[
createLiteral(parameterOffset),
expression
],
location
);
}
export function createMetadataHelper(externalHelpersModuleName: Identifier | undefined, metadataKey: string, metadataValue: Expression) {
return createCall(
createHelperName(externalHelpersModuleName, "__metadata"),
/*typeArguments*/ undefined,
[
createLiteral(metadataKey),
metadataValue
]
);
}
export function createDecorateHelper(externalHelpersModuleName: Identifier | undefined, decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression, location?: TextRange) {
const argumentsArray: Expression[] = [];
argumentsArray.push(createArrayLiteral(decoratorExpressions, /*location*/ undefined, /*multiLine*/ true));
argumentsArray.push(target);
if (memberName) {
argumentsArray.push(memberName);
if (descriptor) {
argumentsArray.push(descriptor);
}
export function requestEmitHelper(helperState: EmitHelperState, helper: EmitHelper) {
if (!contains(helperState.requestedHelpers, helper)) {
helperState.requestedHelpers = append(helperState.requestedHelpers, helper);
}
return createCall(createHelperName(externalHelpersModuleName, "__decorate"), /*typeArguments*/ undefined, argumentsArray, location);
}
export function createAwaiterHelper(externalHelpersModuleName: Identifier | undefined, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) {
const generatorFunc = createFunctionExpression(
/*modifiers*/ undefined,
createToken(SyntaxKind.AsteriskToken),
/*name*/ undefined,
/*typeParameters*/ undefined,
/*parameters*/ [],
/*type*/ undefined,
body
);
// Mark this node as originally an async function
(generatorFunc.emitNode || (generatorFunc.emitNode = {})).flags |= EmitFlags.AsyncFunctionBody;
return createCall(
createHelperName(externalHelpersModuleName, "__awaiter"),
/*typeArguments*/ undefined,
[
createThis(),
hasLexicalArguments ? createIdentifier("arguments") : createVoidZero(),
promiseConstructor ? createExpressionFromEntityName(promiseConstructor) : createVoidZero(),
generatorFunc
]
);
}
export function createHasOwnProperty(target: LeftHandSideExpression, propertyName: Expression) {
return createCall(
createPropertyAccess(target, "hasOwnProperty"),
/*typeArguments*/ undefined,
[propertyName]
);
}
function createObjectCreate(prototype: Expression) {
return createCall(
createPropertyAccess(createIdentifier("Object"), "create"),
/*typeArguments*/ undefined,
[prototype]
);
}
function createGeti(target: LeftHandSideExpression) {
// name => super[name]
return createArrowFunction(
/*modifiers*/ undefined,
/*typeParameters*/ undefined,
[createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "name")],
/*type*/ undefined,
createToken(SyntaxKind.EqualsGreaterThanToken),
createElementAccess(target, createIdentifier("name"))
);
}
function createSeti(target: LeftHandSideExpression) {
// (name, value) => super[name] = value
return createArrowFunction(
/*modifiers*/ undefined,
/*typeParameters*/ undefined,
[
createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "name"),
createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "value")
],
/*type*/ undefined,
createToken(SyntaxKind.EqualsGreaterThanToken),
createAssignment(
createElementAccess(
target,
createIdentifier("name")
),
createIdentifier("value")
)
);
}
export function createAdvancedAsyncSuperHelper() {
// const _super = (function (geti, seti) {
// const cache = Object.create(null);
// return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
// })(name => super[name], (name, value) => super[name] = value);
// const cache = Object.create(null);
const createCache = createVariableStatement(
/*modifiers*/ undefined,
createConstDeclarationList([
createVariableDeclaration(
"cache",
/*type*/ undefined,
createObjectCreate(createNull())
)
])
);
// get value() { return geti(name); }
const getter = createGetAccessor(
/*decorators*/ undefined,
/*modifiers*/ undefined,
"value",
/*parameters*/ [],
/*type*/ undefined,
createBlock([
createReturn(
createCall(
createIdentifier("geti"),
/*typeArguments*/ undefined,
[createIdentifier("name")]
)
)
])
);
// set value(v) { seti(name, v); }
const setter = createSetAccessor(
/*decorators*/ undefined,
/*modifiers*/ undefined,
"value",
[createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "v")],
createBlock([
createStatement(
createCall(
createIdentifier("seti"),
/*typeArguments*/ undefined,
[
createIdentifier("name"),
createIdentifier("v")
]
)
)
])
);
// return name => cache[name] || ...
const getOrCreateAccessorsForName = createReturn(
createArrowFunction(
/*modifiers*/ undefined,
/*typeParameters*/ undefined,
[createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "name")],
/*type*/ undefined,
createToken(SyntaxKind.EqualsGreaterThanToken),
createLogicalOr(
createElementAccess(
createIdentifier("cache"),
createIdentifier("name")
),
createParen(
createAssignment(
createElementAccess(
createIdentifier("cache"),
createIdentifier("name")
),
createObjectLiteral([
getter,
setter
])
)
)
)
)
);
// const _super = (function (geti, seti) {
// const cache = Object.create(null);
// return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
// })(name => super[name], (name, value) => super[name] = value);
return createVariableStatement(
/*modifiers*/ undefined,
createConstDeclarationList([
createVariableDeclaration(
"_super",
/*type*/ undefined,
createCall(
createParen(
createFunctionExpression(
/*modifiers*/ undefined,
/*asteriskToken*/ undefined,
/*name*/ undefined,
/*typeParameters*/ undefined,
[
createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "geti"),
createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, "seti")
],
/*type*/ undefined,
createBlock([
createCache,
getOrCreateAccessorsForName
])
)
),
/*typeArguments*/ undefined,
[
createGeti(createSuper()),
createSeti(createSuper())
]
)
)
])
);
}
export function createSimpleAsyncSuperHelper() {
return createVariableStatement(
/*modifiers*/ undefined,
createConstDeclarationList([
createVariableDeclaration(
"_super",
/*type*/ undefined,
createGeti(createSuper())
)
])
);
}
export interface CallBinding {
@@ -2356,11 +2100,11 @@ namespace ts {
/**
* Ensures "use strict" directive is added
*
* @param node source file
* @param statements An array of statements
*/
export function ensureUseStrict(node: SourceFile): SourceFile {
export function ensureUseStrict(statements: NodeArray<Statement>): NodeArray<Statement> {
let foundUseStrict = false;
for (const statement of node.statements) {
for (const statement of statements) {
if (isPrologueDirective(statement)) {
if (isUseStrictPrologue(statement as ExpressionStatement)) {
foundUseStrict = true;
@@ -2371,13 +2115,15 @@ namespace ts {
break;
}
}
if (!foundUseStrict) {
const statements: Statement[] = [];
statements.push(startOnNewLine(createStatement(createLiteral("use strict"))));
// add "use strict" as the first statement
return updateSourceFileNode(node, statements.concat(node.statements));
return createNodeArray<Statement>([
startOnNewLine(createStatement(createLiteral("use strict"))),
...statements
], statements);
}
return node;
return statements;
}
/**
@@ -2796,12 +2542,21 @@ namespace ts {
}
function mergeEmitNode(sourceEmitNode: EmitNode, destEmitNode: EmitNode) {
const { flags, commentRange, sourceMapRange, tokenSourceMapRanges } = sourceEmitNode;
if (!destEmitNode && (flags || commentRange || sourceMapRange || tokenSourceMapRanges)) destEmitNode = {};
const {
flags,
commentRange,
sourceMapRange,
tokenSourceMapRanges,
constantValue,
helpers
} = sourceEmitNode;
if (!destEmitNode) destEmitNode = {};
if (flags) destEmitNode.flags = flags;
if (commentRange) destEmitNode.commentRange = commentRange;
if (sourceMapRange) destEmitNode.sourceMapRange = sourceMapRange;
if (tokenSourceMapRanges) destEmitNode.tokenSourceMapRanges = mergeTokenSourceMapRanges(tokenSourceMapRanges, destEmitNode.tokenSourceMapRanges);
if (constantValue !== undefined) destEmitNode.constantValue = constantValue;
if (helpers) destEmitNode.helpers = addRange(destEmitNode.helpers, helpers);
return destEmitNode;
}
@@ -2879,6 +2634,16 @@ namespace ts {
return node;
}
/**
* Gets a custom text range to use when emitting source maps.
*
* @param node The node.
*/
export function getSourceMapRange(node: Node) {
const emitNode = node.emitNode;
return (emitNode && emitNode.sourceMapRange) || node;
}
/**
* Sets a custom text range to use when emitting source maps.
*
@@ -2890,6 +2655,18 @@ namespace ts {
return node;
}
/**
* Gets the TextRange to use for source maps for a token of a node.
*
* @param node The node.
* @param token The token.
*/
export function getTokenSourceMapRange(node: Node, token: SyntaxKind) {
const emitNode = node.emitNode;
const tokenSourceMapRanges = emitNode && emitNode.tokenSourceMapRanges;
return tokenSourceMapRanges && tokenSourceMapRanges[token];
}
/**
* Sets the TextRange to use for source maps for a token of a node.
*
@@ -2904,14 +2681,6 @@ namespace ts {
return node;
}
/**
* Sets a custom text range to use when emitting comments.
*/
export function setCommentRange<T extends Node>(node: T, range: TextRange) {
getOrCreateEmitNode(node).commentRange = range;
return node;
}
/**
* Gets a custom text range to use when emitting comments.
*
@@ -2923,25 +2692,11 @@ namespace ts {
}
/**
* Gets a custom text range to use when emitting source maps.
*
* @param node The node.
* Sets a custom text range to use when emitting comments.
*/
export function getSourceMapRange(node: Node) {
const emitNode = node.emitNode;
return (emitNode && emitNode.sourceMapRange) || node;
}
/**
* Gets the TextRange to use for source maps for a token of a node.
*
* @param node The node.
* @param token The token.
*/
export function getTokenSourceMapRange(node: Node, token: SyntaxKind) {
const emitNode = node.emitNode;
const tokenSourceMapRanges = emitNode && emitNode.tokenSourceMapRanges;
return tokenSourceMapRanges && tokenSourceMapRanges[token];
export function setCommentRange<T extends Node>(node: T, range: TextRange) {
getOrCreateEmitNode(node).commentRange = range;
return node;
}
/**
@@ -2961,6 +2716,102 @@ namespace ts {
return node;
}
export function getExternalHelpersModuleName(node: SourceFile) {
const parseNode = getOriginalNode(node, isSourceFile);
const emitNode = parseNode && parseNode.emitNode;
return emitNode && emitNode.externalHelpersModuleName;
}
export function getOrCreateExternalHelpersModuleName(node: SourceFile, compilerOptions: CompilerOptions) {
if (compilerOptions.importHelpers && (isExternalModule(node) || compilerOptions.isolatedModules)) {
const parseNode = getOriginalNode(node, isSourceFile);
const emitNode = getOrCreateEmitNode(parseNode);
return emitNode.externalHelpersModuleName || (emitNode.externalHelpersModuleName = createUniqueName(externalHelpersModuleNameText));
}
}
/**
* Adds an EmitHelper to a node.
*/
export function addEmitHelper<T extends Node>(node: T, helper: EmitHelper): T {
const emitNode = getOrCreateEmitNode(node);
emitNode.helpers = append(emitNode.helpers, helper);
return node;
}
/**
* Adds an EmitHelper to a node.
*/
export function addEmitHelpers<T extends Node>(node: T, helpers: EmitHelper[] | undefined): T {
if (some(helpers)) {
const emitNode = getOrCreateEmitNode(node);
for (const helper of helpers) {
if (!contains(emitNode.helpers, helper)) {
emitNode.helpers = append(emitNode.helpers, helper);
}
}
}
return node;
}
/**
* Removes an EmitHelper from a node.
*/
export function removeEmitHelper(node: Node, helper: EmitHelper): boolean {
const emitNode = node.emitNode;
if (emitNode) {
const helpers = emitNode.helpers;
if (helpers) {
return orderedRemoveItem(helpers, helper);
}
}
return false;
}
/**
* Gets the EmitHelpers of a node.
*/
export function getEmitHelpers(node: Node): EmitHelper[] | undefined {
const emitNode = node.emitNode;
return emitNode && emitNode.helpers;
}
/**
* Moves matching emit helpers from a source node to a target node.
*/
export function moveEmitHelpers(source: Node, target: Node, predicate: (helper: EmitHelper) => boolean) {
const sourceEmitNode = source.emitNode;
const sourceEmitHelpers = sourceEmitNode && sourceEmitNode.helpers;
if (!some(sourceEmitHelpers)) return;
const targetEmitNode = getOrCreateEmitNode(target);
let helpersRemoved = 0;
for (let i = 0; i < sourceEmitHelpers.length; i++) {
const helper = sourceEmitHelpers[i];
if (predicate(helper)) {
helpersRemoved++;
if (!contains(targetEmitNode.helpers, helper)) {
targetEmitNode.helpers = append(targetEmitNode.helpers, helper);
}
}
else if (helpersRemoved > 0) {
sourceEmitHelpers[i - helpersRemoved] = helper;
}
}
if (helpersRemoved > 0) {
sourceEmitHelpers.length -= helpersRemoved;
}
}
export function compareEmitHelpers(x: EmitHelper, y: EmitHelper) {
if (x === y) return Comparison.EqualTo;
if (x.priority === y.priority) return Comparison.EqualTo;
if (x.priority === undefined) return Comparison.GreaterThan;
if (y.priority === undefined) return Comparison.LessThan;
return compareValues(x.priority, y.priority);
}
export function setTextRange<T extends TextRange>(node: T, location: TextRange): T {
if (location) {
node.pos = location.pos;
@@ -3055,4 +2906,164 @@ namespace ts {
function tryGetModuleNameFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration, host: EmitHost, resolver: EmitResolver, compilerOptions: CompilerOptions) {
return tryGetModuleNameFromFile(resolver.getExternalModuleFileFromDeclaration(declaration), host, compilerOptions);
}
export interface ExternalModuleInfo {
externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; // imports of other external modules
externalHelpersImportDeclaration: ImportDeclaration | undefined; // import of external helpers
exportSpecifiers: Map<ExportSpecifier[]>; // export specifiers by name
exportedBindings: Map<Identifier[]>; // exported names of local declarations
exportedNames: Identifier[]; // all exported names local to module
exportEquals: ExportAssignment | undefined; // an export= declaration if one was present
hasExportStarsToExportValues: boolean; // whether this module contains export*
}
export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver): ExternalModuleInfo {
const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = [];
const exportSpecifiers = createMap<ExportSpecifier[]>();
const exportedBindings = createMap<Identifier[]>();
const uniqueExports = createMap<Identifier>();
let hasExportDefault = false;
let exportEquals: ExportAssignment = undefined;
let hasExportStarsToExportValues = false;
const externalHelpersModuleName = getExternalHelpersModuleName(sourceFile);
const externalHelpersImportDeclaration = externalHelpersModuleName && createImportDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
createImportClause(/*name*/ undefined, createNamespaceImport(externalHelpersModuleName)),
createLiteral(externalHelpersModuleNameText));
if (externalHelpersImportDeclaration) {
externalImports.push(externalHelpersImportDeclaration);
}
for (const node of sourceFile.statements) {
switch (node.kind) {
case SyntaxKind.ImportDeclaration:
// import "mod"
// import x from "mod"
// import * as x from "mod"
// import { x, y } from "mod"
externalImports.push(<ImportDeclaration>node);
break;
case SyntaxKind.ImportEqualsDeclaration:
if ((<ImportEqualsDeclaration>node).moduleReference.kind === SyntaxKind.ExternalModuleReference) {
// import x = require("mod")
externalImports.push(<ImportEqualsDeclaration>node);
}
break;
case SyntaxKind.ExportDeclaration:
if ((<ExportDeclaration>node).moduleSpecifier) {
if (!(<ExportDeclaration>node).exportClause) {
// export * from "mod"
externalImports.push(<ExportDeclaration>node);
hasExportStarsToExportValues = true;
}
else {
// export { x, y } from "mod"
externalImports.push(<ExportDeclaration>node);
}
}
else {
// export { x, y }
for (const specifier of (<ExportDeclaration>node).exportClause.elements) {
if (!uniqueExports[specifier.name.text]) {
const name = specifier.propertyName || specifier.name;
multiMapAdd(exportSpecifiers, name.text, specifier);
const decl = resolver.getReferencedImportDeclaration(name)
|| resolver.getReferencedValueDeclaration(name);
if (decl) {
multiMapAdd(exportedBindings, getOriginalNodeId(decl), specifier.name);
}
uniqueExports[specifier.name.text] = specifier.name;
}
}
}
break;
case SyntaxKind.ExportAssignment:
if ((<ExportAssignment>node).isExportEquals && !exportEquals) {
// export = x
exportEquals = <ExportAssignment>node;
}
break;
case SyntaxKind.VariableStatement:
if (hasModifier(node, ModifierFlags.Export)) {
for (const decl of (<VariableStatement>node).declarationList.declarations) {
collectExportedVariableInfo(decl, uniqueExports);
}
}
break;
case SyntaxKind.FunctionDeclaration:
if (hasModifier(node, ModifierFlags.Export)) {
if (hasModifier(node, ModifierFlags.Default)) {
// export default function() { }
if (!hasExportDefault) {
multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(<FunctionDeclaration>node));
hasExportDefault = true;
}
}
else {
// export function x() { }
const name = (<FunctionDeclaration>node).name;
if (!uniqueExports[name.text]) {
multiMapAdd(exportedBindings, getOriginalNodeId(node), name);
uniqueExports[name.text] = name;
}
}
}
break;
case SyntaxKind.ClassDeclaration:
if (hasModifier(node, ModifierFlags.Export)) {
if (hasModifier(node, ModifierFlags.Default)) {
// export default class { }
if (!hasExportDefault) {
multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(<ClassDeclaration>node));
hasExportDefault = true;
}
}
else {
// export class x { }
const name = (<ClassDeclaration>node).name;
if (!uniqueExports[name.text]) {
multiMapAdd(exportedBindings, getOriginalNodeId(node), name);
uniqueExports[name.text] = name;
}
}
}
break;
}
}
let exportedNames: Identifier[];
for (const key in uniqueExports) {
exportedNames = ts.append(exportedNames, uniqueExports[key]);
}
return { externalImports, exportSpecifiers, exportEquals, hasExportStarsToExportValues, exportedBindings, exportedNames, externalHelpersImportDeclaration };
}
function collectExportedVariableInfo(decl: VariableDeclaration | BindingElement, uniqueExports: Map<Identifier>) {
if (isBindingPattern(decl.name)) {
for (const element of decl.name.elements) {
if (!isOmittedExpression(element)) {
collectExportedVariableInfo(element, uniqueExports);
}
}
}
else if (!isGeneratedIdentifier(decl.name)) {
if (!uniqueExports[decl.name.text]) {
uniqueExports[decl.name.text] = decl.name;
}
}
}
}

View File

@@ -3,7 +3,6 @@
/*@internal*/
namespace ts {
const enum ES2015SubstitutionFlags {
/** Enables substitutions for captured `this` */
CapturedThis = 1 << 0,
@@ -170,6 +169,7 @@ namespace ts {
hoistVariableDeclaration,
} = context;
const compilerOptions = context.getCompilerOptions();
const resolver = context.getEmitResolver();
const previousOnSubstituteNode = context.onSubstituteNode;
const previousOnEmitNode = context.onEmitNode;
@@ -187,6 +187,7 @@ namespace ts {
let enclosingNonArrowFunction: FunctionLikeDeclaration;
let enclosingNonAsyncFunctionBody: FunctionLikeDeclaration | ClassElement;
let isInConstructorWithCapturedSuper: boolean;
let helperState: EmitHelperState;
/**
* Used to track if we are emitting body of the converted loop
@@ -209,7 +210,15 @@ namespace ts {
currentSourceFile = node;
currentText = node.text;
return visitNode(node, visitor, isSourceFile);
helperState = { currentSourceFile, compilerOptions };
const visited = visitNode(node, visitor, isSourceFile);
addEmitHelpers(visited, helperState.requestedHelpers);
currentSourceFile = undefined;
currentText = undefined;
helperState = undefined;
return visited;
}
function visitor(node: Node): VisitResult<Node> {
@@ -778,7 +787,7 @@ namespace ts {
if (extendsClauseElement) {
statements.push(
createStatement(
createExtendsHelper(currentSourceFile.externalHelpersModuleName, getLocalName(node)),
createExtendsHelper(helperState, getLocalName(node)),
/*location*/ extendsClauseElement
)
);
@@ -3274,4 +3283,28 @@ namespace ts {
return isIdentifier(expression) && expression === parameter.name;
}
}
function createExtendsHelper(helperState: EmitHelperState, name: Identifier) {
requestEmitHelper(helperState, extendsHelper);
return createCall(
getHelperName(helperState, "__extends"),
/*typeArguments*/ undefined,
[
name,
createIdentifier("_super")
]
);
}
const extendsHelper: EmitHelper = {
name: "typescript:extends",
scoped: false,
priority: 0,
text: `
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};`
};
}

View File

@@ -5,13 +5,12 @@
namespace ts {
type SuperContainer = ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration;
const enum ES2017SubstitutionFlags {
/** Enables substitutions for async methods with `super` calls. */
AsyncMethodsWithSuper = 1 << 0
}
export function transformES2017(context: TransformationContext) {
const enum ES2017SubstitutionFlags {
/** Enables substitutions for async methods with `super` calls. */
AsyncMethodsWithSuper = 1 << 0
}
const {
startLexicalEnvironment,
endLexicalEnvironment,
@@ -22,7 +21,9 @@ namespace ts {
const languageVersion = getEmitScriptTarget(compilerOptions);
// These variables contain state that changes as we descend into the tree.
let currentSourceFileExternalHelpersModuleName: Identifier;
let currentSourceFile: SourceFile;
let helperState: EmitHelperState;
/**
* Keeps track of whether expression substitution has been enabled for specific edge cases.
* They are persisted between each SourceFile transformation and should not be reset.
@@ -58,9 +59,16 @@ namespace ts {
return node;
}
currentSourceFileExternalHelpersModuleName = node.externalHelpersModuleName;
currentSourceFile = node;
helperState = { currentSourceFile, compilerOptions };
return visitEachChild(node, visitor, context);
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, helperState.requestedHelpers);
currentSourceFile = undefined;
helperState = undefined;
return visited;
}
function visitor(node: Node): VisitResult<Node> {
@@ -107,11 +115,11 @@ namespace ts {
}
/**
* Visits an await expression.
* Visits an AwaitExpression node.
*
* This function will be called any time a ES2017 await expression is encountered.
*
* @param node The await expression node.
* @param node The node to visit.
*/
function visitAwaitExpression(node: AwaitExpression): Expression {
return setOriginalNode(
@@ -125,17 +133,15 @@ namespace ts {
}
/**
* Visits a method declaration of a class.
* Visits a MethodDeclaration node.
*
* This function will be called when one of the following conditions are met:
* - The node is marked as async
*
* @param node The method node.
* @param node The node to visit.
*/
function visitMethodDeclaration(node: MethodDeclaration) {
if (!isAsyncFunctionLike(node)) {
return node;
}
Debug.assert(hasModifier(node, ModifierFlags.Async));
const method = createMethod(
/*decorators*/ undefined,
visitNodes(node.modifiers, visitor, isModifier),
@@ -150,25 +156,20 @@ namespace ts {
// While we emit the source map for the node after skipping decorators and modifiers,
// we need to emit the comments for the original range.
setCommentRange(method, node);
setSourceMapRange(method, moveRangePastDecorators(node));
setOriginalNode(method, node);
return method;
}
/**
* Visits a function declaration.
* Visits a FunctionDeclaration node.
*
* This function will be called when one of the following conditions are met:
* - The node is marked async
*
* @param node The function node.
* @param node The node to visit.
*/
function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult<Statement> {
if (!isAsyncFunctionLike(node)) {
return node;
}
Debug.assert(hasModifier(node, ModifierFlags.Async));
const func = createFunctionDeclaration(
/*decorators*/ undefined,
visitNodes(node.modifiers, visitor, isModifier),
@@ -180,23 +181,21 @@ namespace ts {
transformFunctionBody(node),
/*location*/ node
);
setOriginalNode(func, node);
setOriginalNode(func, node);
return func;
}
/**
* Visits a function expression node.
* Visits a FunctionExpression node.
*
* This function will be called when one of the following conditions are met:
* - The node is marked async
*
* @param node The function expression node.
* @param node The node to visit.
*/
function visitFunctionExpression(node: FunctionExpression): Expression {
if (!isAsyncFunctionLike(node)) {
return node;
}
Debug.assert(hasModifier(node, ModifierFlags.Async));
if (nodeIsMissing(node.body)) {
return createOmittedExpression();
}
@@ -213,19 +212,19 @@ namespace ts {
);
setOriginalNode(func, node);
return func;
}
/**
* @remarks
* Visits an ArrowFunction.
*
* This function will be called when one of the following conditions are met:
* - The node is marked async
*
* @param node The node to visit.
*/
function visitArrowFunction(node: ArrowFunction) {
if (!isAsyncFunctionLike(node)) {
return node;
}
Debug.assert(hasModifier(node, ModifierFlags.Async));
const func = createArrowFunction(
visitNodes(node.modifiers, visitor, isModifier),
/*typeParameters*/ undefined,
@@ -237,7 +236,6 @@ namespace ts {
);
setOriginalNode(func, node);
return func;
}
@@ -280,7 +278,7 @@ namespace ts {
statements.push(
createReturn(
createAwaiterHelper(
currentSourceFileExternalHelpersModuleName,
helperState,
hasLexicalArguments,
promiseConstructor,
transformFunctionBodyWorker(<Block>node.body, statementOffset)
@@ -295,11 +293,11 @@ namespace ts {
if (languageVersion >= ScriptTarget.ES2015) {
if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuperBinding) {
enableSubstitutionForAsyncMethodsWithSuper();
setEmitFlags(block, EmitFlags.EmitAdvancedSuperHelper);
addEmitHelper(block, advancedAsyncSuperHelper);
}
else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.AsyncMethodWithSuper) {
enableSubstitutionForAsyncMethodsWithSuper();
setEmitFlags(block, EmitFlags.EmitSuperHelper);
addEmitHelper(block, asyncSuperHelper);
}
}
@@ -307,7 +305,7 @@ namespace ts {
}
else {
return createAwaiterHelper(
currentSourceFileExternalHelpersModuleName,
helperState,
hasLexicalArguments,
promiseConstructor,
<Block>transformConciseBodyWorker(node.body, /*forceBlockFunctionBody*/ true)
@@ -507,4 +505,63 @@ namespace ts {
&& resolver.getNodeCheckFlags(currentSuperContainer) & (NodeCheckFlags.AsyncMethodWithSuper | NodeCheckFlags.AsyncMethodWithSuperBinding);
}
}
function createAwaiterHelper(helperState: EmitHelperState, hasLexicalArguments: boolean, promiseConstructor: EntityName | Expression, body: Block) {
const generatorFunc = createFunctionExpression(
/*modifiers*/ undefined,
createToken(SyntaxKind.AsteriskToken),
/*name*/ undefined,
/*typeParameters*/ undefined,
/*parameters*/ [],
/*type*/ undefined,
body
);
// Mark this node as originally an async function
(generatorFunc.emitNode || (generatorFunc.emitNode = {})).flags |= EmitFlags.AsyncFunctionBody;
requestEmitHelper(helperState, awaiterHelper);
return createCall(
getHelperName(helperState, "__awaiter"),
/*typeArguments*/ undefined,
[
createThis(),
hasLexicalArguments ? createIdentifier("arguments") : createVoidZero(),
promiseConstructor ? createExpressionFromEntityName(promiseConstructor) : createVoidZero(),
generatorFunc
]
);
}
const awaiterHelper: EmitHelper = {
name: "typescript:awaiter",
scoped: false,
priority: 5,
text: `
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments)).next());
});
};`
};
const asyncSuperHelper: EmitHelper = {
name: "typescript:async-super",
scoped: true,
text: `
const _super = name => super[name];`
};
const advancedAsyncSuperHelper: EmitHelper = {
name: "typescript:advanced-async-super",
scoped: true,
text: `
const _super = (function (geti, seti) {
const cache = Object.create(null);
return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
})(name => super[name], (name, value) => super[name] = value);`
};
}

View File

@@ -242,6 +242,7 @@ namespace ts {
let currentSourceFile: SourceFile;
let renamedCatchVariables: Map<boolean>;
let renamedCatchVariableDeclarations: Map<Identifier>;
let helperState: EmitHelperState;
let inGeneratorFunctionBody: boolean;
let inStatementContainingYield: boolean;
@@ -291,17 +292,20 @@ namespace ts {
return transformSourceFile;
function transformSourceFile(node: SourceFile) {
if (isDeclarationFile(node)) {
if (isDeclarationFile(node)
|| (node.transformFlags & TransformFlags.ContainsGenerator) === 0) {
return node;
}
if (node.transformFlags & TransformFlags.ContainsGenerator) {
currentSourceFile = node;
node = visitEachChild(node, visitor, context);
currentSourceFile = undefined;
}
currentSourceFile = node;
helperState = { currentSourceFile, compilerOptions };
return node;
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, helperState.requestedHelpers);
currentSourceFile = undefined;
helperState = undefined;
return visited;
}
/**
@@ -2585,28 +2589,24 @@ namespace ts {
withBlockStack = undefined;
const buildResult = buildStatements();
return createCall(
createHelperName(currentSourceFile.externalHelpersModuleName, "__generator"),
/*typeArguments*/ undefined,
[
createThis(),
setEmitFlags(
createFunctionExpression(
/*modifiers*/ undefined,
/*asteriskToken*/ undefined,
/*name*/ undefined,
/*typeParameters*/ undefined,
[createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, state)],
/*type*/ undefined,
createBlock(
buildResult,
/*location*/ undefined,
/*multiLine*/ buildResult.length > 0
)
),
EmitFlags.ReuseTempVariableScope
)
]
return createGeneratorHelper(
helperState,
setEmitFlags(
createFunctionExpression(
/*modifiers*/ undefined,
/*asteriskToken*/ undefined,
/*name*/ undefined,
/*typeParameters*/ undefined,
[createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, state)],
/*type*/ undefined,
createBlock(
buildResult,
/*location*/ undefined,
/*multiLine*/ buildResult.length > 0
)
),
EmitFlags.ReuseTempVariableScope
)
);
}
@@ -3082,4 +3082,105 @@ namespace ts {
);
}
}
function createGeneratorHelper(helperState: EmitHelperState, body: FunctionExpression) {
requestEmitHelper(helperState, generatorHelper);
return createCall(
getHelperName(helperState, "__generator"),
/*typeArguments*/ undefined,
[createThis(), body]);
}
// The __generator helper is used by down-level transformations to emulate the runtime
// semantics of an ES2015 generator function. When called, this helper returns an
// object that implements the Iterator protocol, in that it has `next`, `return`, and
// `throw` methods that step through the generator when invoked.
//
// parameters:
// thisArg The value to use as the `this` binding for the transformed generator body.
// body A function that acts as the transformed generator body.
//
// variables:
// _ Persistent state for the generator that is shared between the helper and the
// generator body. The state object has the following members:
// sent() - A method that returns or throws the current completion value.
// label - The next point at which to resume evaluation of the generator body.
// trys - A stack of protected regions (try/catch/finally blocks).
// ops - A stack of pending instructions when inside of a finally block.
// f A value indicating whether the generator is executing.
// y An iterator to delegate for a yield*.
// t A temporary variable that holds one of the following values (note that these
// cases do not overlap):
// - The completion value when resuming from a `yield` or `yield*`.
// - The error value for a catch block.
// - The current protected region (array of try/catch/finally/end labels).
// - The verb (`next`, `throw`, or `return` method) to delegate to the expression
// of a `yield*`.
// - The result of evaluating the verb delegated to the expression of a `yield*`.
//
// functions:
// verb(n) Creates a bound callback to the `step` function for opcode `n`.
// step(op) Evaluates opcodes in a generator body until execution is suspended or
// completed.
//
// The __generator helper understands a limited set of instructions:
// 0: next(value?) - Start or resume the generator with the specified value.
// 1: throw(error) - Resume the generator with an exception. If the generator is
// suspended inside of one or more protected regions, evaluates
// any intervening finally blocks between the current label and
// the nearest catch block or function boundary. If uncaught, the
// exception is thrown to the caller.
// 2: return(value?) - Resume the generator as if with a return. If the generator is
// suspended inside of one or more protected regions, evaluates any
// intervening finally blocks.
// 3: break(label) - Jump to the specified label. If the label is outside of the
// current protected region, evaluates any intervening finally
// blocks.
// 4: yield(value?) - Yield execution to the caller with an optional value. When
// resumed, the generator will continue at the next label.
// 5: yield*(value) - Delegates evaluation to the supplied iterator. When
// delegation completes, the generator will continue at the next
// label.
// 6: catch(error) - Handles an exception thrown from within the generator body. If
// the current label is inside of one or more protected regions,
// evaluates any intervening finally blocks between the current
// label and the nearest catch block or function boundary. If
// uncaught, the exception is thrown to the caller.
// 7: endfinally - Ends a finally block, resuming the last instruction prior to
// entering a finally block.
//
// For examples of how these are used, see the comments in ./transformers/generators.ts
const generatorHelper: EmitHelper = {
name: "typescript:generator",
scoped: false,
priority: 6,
text: `
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t;
return { next: verb(0), "throw": verb(1), "return": verb(2) };
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [0, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};`
};
}

View File

@@ -3,11 +3,11 @@
/*@internal*/
namespace ts {
const entities: Map<number> = createEntitiesMap();
export function transformJsx(context: TransformationContext) {
const compilerOptions = context.getCompilerOptions();
let currentSourceFile: SourceFile;
let helperState: EmitHelperState;
return transformSourceFile;
/**
@@ -21,9 +21,14 @@ namespace ts {
}
currentSourceFile = node;
node = visitEachChild(node, visitor, context);
helperState = { currentSourceFile, compilerOptions };
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, helperState.requestedHelpers);
currentSourceFile = undefined;
return node;
helperState = undefined;
return visited;
}
function visitor(node: Node): VisitResult<Node> {
@@ -109,8 +114,10 @@ namespace ts {
// Either emit one big object literal (no spread attribs), or
// a call to the __assign helper.
objectProperties = singleOrUndefined(segments)
|| createAssignHelper(currentSourceFile.externalHelpersModuleName, segments);
objectProperties = singleOrUndefined(segments);
if (!objectProperties) {
objectProperties = createAssignHelper(helperState, segments);
}
}
const element = createReactCreateElement(
@@ -275,261 +282,283 @@ namespace ts {
}
}
function createEntitiesMap(): Map<number> {
return createMap<number>({
"quot": 0x0022,
"amp": 0x0026,
"apos": 0x0027,
"lt": 0x003C,
"gt": 0x003E,
"nbsp": 0x00A0,
"iexcl": 0x00A1,
"cent": 0x00A2,
"pound": 0x00A3,
"curren": 0x00A4,
"yen": 0x00A5,
"brvbar": 0x00A6,
"sect": 0x00A7,
"uml": 0x00A8,
"copy": 0x00A9,
"ordf": 0x00AA,
"laquo": 0x00AB,
"not": 0x00AC,
"shy": 0x00AD,
"reg": 0x00AE,
"macr": 0x00AF,
"deg": 0x00B0,
"plusmn": 0x00B1,
"sup2": 0x00B2,
"sup3": 0x00B3,
"acute": 0x00B4,
"micro": 0x00B5,
"para": 0x00B6,
"middot": 0x00B7,
"cedil": 0x00B8,
"sup1": 0x00B9,
"ordm": 0x00BA,
"raquo": 0x00BB,
"frac14": 0x00BC,
"frac12": 0x00BD,
"frac34": 0x00BE,
"iquest": 0x00BF,
"Agrave": 0x00C0,
"Aacute": 0x00C1,
"Acirc": 0x00C2,
"Atilde": 0x00C3,
"Auml": 0x00C4,
"Aring": 0x00C5,
"AElig": 0x00C6,
"Ccedil": 0x00C7,
"Egrave": 0x00C8,
"Eacute": 0x00C9,
"Ecirc": 0x00CA,
"Euml": 0x00CB,
"Igrave": 0x00CC,
"Iacute": 0x00CD,
"Icirc": 0x00CE,
"Iuml": 0x00CF,
"ETH": 0x00D0,
"Ntilde": 0x00D1,
"Ograve": 0x00D2,
"Oacute": 0x00D3,
"Ocirc": 0x00D4,
"Otilde": 0x00D5,
"Ouml": 0x00D6,
"times": 0x00D7,
"Oslash": 0x00D8,
"Ugrave": 0x00D9,
"Uacute": 0x00DA,
"Ucirc": 0x00DB,
"Uuml": 0x00DC,
"Yacute": 0x00DD,
"THORN": 0x00DE,
"szlig": 0x00DF,
"agrave": 0x00E0,
"aacute": 0x00E1,
"acirc": 0x00E2,
"atilde": 0x00E3,
"auml": 0x00E4,
"aring": 0x00E5,
"aelig": 0x00E6,
"ccedil": 0x00E7,
"egrave": 0x00E8,
"eacute": 0x00E9,
"ecirc": 0x00EA,
"euml": 0x00EB,
"igrave": 0x00EC,
"iacute": 0x00ED,
"icirc": 0x00EE,
"iuml": 0x00EF,
"eth": 0x00F0,
"ntilde": 0x00F1,
"ograve": 0x00F2,
"oacute": 0x00F3,
"ocirc": 0x00F4,
"otilde": 0x00F5,
"ouml": 0x00F6,
"divide": 0x00F7,
"oslash": 0x00F8,
"ugrave": 0x00F9,
"uacute": 0x00FA,
"ucirc": 0x00FB,
"uuml": 0x00FC,
"yacute": 0x00FD,
"thorn": 0x00FE,
"yuml": 0x00FF,
"OElig": 0x0152,
"oelig": 0x0153,
"Scaron": 0x0160,
"scaron": 0x0161,
"Yuml": 0x0178,
"fnof": 0x0192,
"circ": 0x02C6,
"tilde": 0x02DC,
"Alpha": 0x0391,
"Beta": 0x0392,
"Gamma": 0x0393,
"Delta": 0x0394,
"Epsilon": 0x0395,
"Zeta": 0x0396,
"Eta": 0x0397,
"Theta": 0x0398,
"Iota": 0x0399,
"Kappa": 0x039A,
"Lambda": 0x039B,
"Mu": 0x039C,
"Nu": 0x039D,
"Xi": 0x039E,
"Omicron": 0x039F,
"Pi": 0x03A0,
"Rho": 0x03A1,
"Sigma": 0x03A3,
"Tau": 0x03A4,
"Upsilon": 0x03A5,
"Phi": 0x03A6,
"Chi": 0x03A7,
"Psi": 0x03A8,
"Omega": 0x03A9,
"alpha": 0x03B1,
"beta": 0x03B2,
"gamma": 0x03B3,
"delta": 0x03B4,
"epsilon": 0x03B5,
"zeta": 0x03B6,
"eta": 0x03B7,
"theta": 0x03B8,
"iota": 0x03B9,
"kappa": 0x03BA,
"lambda": 0x03BB,
"mu": 0x03BC,
"nu": 0x03BD,
"xi": 0x03BE,
"omicron": 0x03BF,
"pi": 0x03C0,
"rho": 0x03C1,
"sigmaf": 0x03C2,
"sigma": 0x03C3,
"tau": 0x03C4,
"upsilon": 0x03C5,
"phi": 0x03C6,
"chi": 0x03C7,
"psi": 0x03C8,
"omega": 0x03C9,
"thetasym": 0x03D1,
"upsih": 0x03D2,
"piv": 0x03D6,
"ensp": 0x2002,
"emsp": 0x2003,
"thinsp": 0x2009,
"zwnj": 0x200C,
"zwj": 0x200D,
"lrm": 0x200E,
"rlm": 0x200F,
"ndash": 0x2013,
"mdash": 0x2014,
"lsquo": 0x2018,
"rsquo": 0x2019,
"sbquo": 0x201A,
"ldquo": 0x201C,
"rdquo": 0x201D,
"bdquo": 0x201E,
"dagger": 0x2020,
"Dagger": 0x2021,
"bull": 0x2022,
"hellip": 0x2026,
"permil": 0x2030,
"prime": 0x2032,
"Prime": 0x2033,
"lsaquo": 0x2039,
"rsaquo": 0x203A,
"oline": 0x203E,
"frasl": 0x2044,
"euro": 0x20AC,
"image": 0x2111,
"weierp": 0x2118,
"real": 0x211C,
"trade": 0x2122,
"alefsym": 0x2135,
"larr": 0x2190,
"uarr": 0x2191,
"rarr": 0x2192,
"darr": 0x2193,
"harr": 0x2194,
"crarr": 0x21B5,
"lArr": 0x21D0,
"uArr": 0x21D1,
"rArr": 0x21D2,
"dArr": 0x21D3,
"hArr": 0x21D4,
"forall": 0x2200,
"part": 0x2202,
"exist": 0x2203,
"empty": 0x2205,
"nabla": 0x2207,
"isin": 0x2208,
"notin": 0x2209,
"ni": 0x220B,
"prod": 0x220F,
"sum": 0x2211,
"minus": 0x2212,
"lowast": 0x2217,
"radic": 0x221A,
"prop": 0x221D,
"infin": 0x221E,
"ang": 0x2220,
"and": 0x2227,
"or": 0x2228,
"cap": 0x2229,
"cup": 0x222A,
"int": 0x222B,
"there4": 0x2234,
"sim": 0x223C,
"cong": 0x2245,
"asymp": 0x2248,
"ne": 0x2260,
"equiv": 0x2261,
"le": 0x2264,
"ge": 0x2265,
"sub": 0x2282,
"sup": 0x2283,
"nsub": 0x2284,
"sube": 0x2286,
"supe": 0x2287,
"oplus": 0x2295,
"otimes": 0x2297,
"perp": 0x22A5,
"sdot": 0x22C5,
"lceil": 0x2308,
"rceil": 0x2309,
"lfloor": 0x230A,
"rfloor": 0x230B,
"lang": 0x2329,
"rang": 0x232A,
"loz": 0x25CA,
"spades": 0x2660,
"clubs": 0x2663,
"hearts": 0x2665,
"diams": 0x2666
});
const entities = createMap<number>({
"quot": 0x0022,
"amp": 0x0026,
"apos": 0x0027,
"lt": 0x003C,
"gt": 0x003E,
"nbsp": 0x00A0,
"iexcl": 0x00A1,
"cent": 0x00A2,
"pound": 0x00A3,
"curren": 0x00A4,
"yen": 0x00A5,
"brvbar": 0x00A6,
"sect": 0x00A7,
"uml": 0x00A8,
"copy": 0x00A9,
"ordf": 0x00AA,
"laquo": 0x00AB,
"not": 0x00AC,
"shy": 0x00AD,
"reg": 0x00AE,
"macr": 0x00AF,
"deg": 0x00B0,
"plusmn": 0x00B1,
"sup2": 0x00B2,
"sup3": 0x00B3,
"acute": 0x00B4,
"micro": 0x00B5,
"para": 0x00B6,
"middot": 0x00B7,
"cedil": 0x00B8,
"sup1": 0x00B9,
"ordm": 0x00BA,
"raquo": 0x00BB,
"frac14": 0x00BC,
"frac12": 0x00BD,
"frac34": 0x00BE,
"iquest": 0x00BF,
"Agrave": 0x00C0,
"Aacute": 0x00C1,
"Acirc": 0x00C2,
"Atilde": 0x00C3,
"Auml": 0x00C4,
"Aring": 0x00C5,
"AElig": 0x00C6,
"Ccedil": 0x00C7,
"Egrave": 0x00C8,
"Eacute": 0x00C9,
"Ecirc": 0x00CA,
"Euml": 0x00CB,
"Igrave": 0x00CC,
"Iacute": 0x00CD,
"Icirc": 0x00CE,
"Iuml": 0x00CF,
"ETH": 0x00D0,
"Ntilde": 0x00D1,
"Ograve": 0x00D2,
"Oacute": 0x00D3,
"Ocirc": 0x00D4,
"Otilde": 0x00D5,
"Ouml": 0x00D6,
"times": 0x00D7,
"Oslash": 0x00D8,
"Ugrave": 0x00D9,
"Uacute": 0x00DA,
"Ucirc": 0x00DB,
"Uuml": 0x00DC,
"Yacute": 0x00DD,
"THORN": 0x00DE,
"szlig": 0x00DF,
"agrave": 0x00E0,
"aacute": 0x00E1,
"acirc": 0x00E2,
"atilde": 0x00E3,
"auml": 0x00E4,
"aring": 0x00E5,
"aelig": 0x00E6,
"ccedil": 0x00E7,
"egrave": 0x00E8,
"eacute": 0x00E9,
"ecirc": 0x00EA,
"euml": 0x00EB,
"igrave": 0x00EC,
"iacute": 0x00ED,
"icirc": 0x00EE,
"iuml": 0x00EF,
"eth": 0x00F0,
"ntilde": 0x00F1,
"ograve": 0x00F2,
"oacute": 0x00F3,
"ocirc": 0x00F4,
"otilde": 0x00F5,
"ouml": 0x00F6,
"divide": 0x00F7,
"oslash": 0x00F8,
"ugrave": 0x00F9,
"uacute": 0x00FA,
"ucirc": 0x00FB,
"uuml": 0x00FC,
"yacute": 0x00FD,
"thorn": 0x00FE,
"yuml": 0x00FF,
"OElig": 0x0152,
"oelig": 0x0153,
"Scaron": 0x0160,
"scaron": 0x0161,
"Yuml": 0x0178,
"fnof": 0x0192,
"circ": 0x02C6,
"tilde": 0x02DC,
"Alpha": 0x0391,
"Beta": 0x0392,
"Gamma": 0x0393,
"Delta": 0x0394,
"Epsilon": 0x0395,
"Zeta": 0x0396,
"Eta": 0x0397,
"Theta": 0x0398,
"Iota": 0x0399,
"Kappa": 0x039A,
"Lambda": 0x039B,
"Mu": 0x039C,
"Nu": 0x039D,
"Xi": 0x039E,
"Omicron": 0x039F,
"Pi": 0x03A0,
"Rho": 0x03A1,
"Sigma": 0x03A3,
"Tau": 0x03A4,
"Upsilon": 0x03A5,
"Phi": 0x03A6,
"Chi": 0x03A7,
"Psi": 0x03A8,
"Omega": 0x03A9,
"alpha": 0x03B1,
"beta": 0x03B2,
"gamma": 0x03B3,
"delta": 0x03B4,
"epsilon": 0x03B5,
"zeta": 0x03B6,
"eta": 0x03B7,
"theta": 0x03B8,
"iota": 0x03B9,
"kappa": 0x03BA,
"lambda": 0x03BB,
"mu": 0x03BC,
"nu": 0x03BD,
"xi": 0x03BE,
"omicron": 0x03BF,
"pi": 0x03C0,
"rho": 0x03C1,
"sigmaf": 0x03C2,
"sigma": 0x03C3,
"tau": 0x03C4,
"upsilon": 0x03C5,
"phi": 0x03C6,
"chi": 0x03C7,
"psi": 0x03C8,
"omega": 0x03C9,
"thetasym": 0x03D1,
"upsih": 0x03D2,
"piv": 0x03D6,
"ensp": 0x2002,
"emsp": 0x2003,
"thinsp": 0x2009,
"zwnj": 0x200C,
"zwj": 0x200D,
"lrm": 0x200E,
"rlm": 0x200F,
"ndash": 0x2013,
"mdash": 0x2014,
"lsquo": 0x2018,
"rsquo": 0x2019,
"sbquo": 0x201A,
"ldquo": 0x201C,
"rdquo": 0x201D,
"bdquo": 0x201E,
"dagger": 0x2020,
"Dagger": 0x2021,
"bull": 0x2022,
"hellip": 0x2026,
"permil": 0x2030,
"prime": 0x2032,
"Prime": 0x2033,
"lsaquo": 0x2039,
"rsaquo": 0x203A,
"oline": 0x203E,
"frasl": 0x2044,
"euro": 0x20AC,
"image": 0x2111,
"weierp": 0x2118,
"real": 0x211C,
"trade": 0x2122,
"alefsym": 0x2135,
"larr": 0x2190,
"uarr": 0x2191,
"rarr": 0x2192,
"darr": 0x2193,
"harr": 0x2194,
"crarr": 0x21B5,
"lArr": 0x21D0,
"uArr": 0x21D1,
"rArr": 0x21D2,
"dArr": 0x21D3,
"hArr": 0x21D4,
"forall": 0x2200,
"part": 0x2202,
"exist": 0x2203,
"empty": 0x2205,
"nabla": 0x2207,
"isin": 0x2208,
"notin": 0x2209,
"ni": 0x220B,
"prod": 0x220F,
"sum": 0x2211,
"minus": 0x2212,
"lowast": 0x2217,
"radic": 0x221A,
"prop": 0x221D,
"infin": 0x221E,
"ang": 0x2220,
"and": 0x2227,
"or": 0x2228,
"cap": 0x2229,
"cup": 0x222A,
"int": 0x222B,
"there4": 0x2234,
"sim": 0x223C,
"cong": 0x2245,
"asymp": 0x2248,
"ne": 0x2260,
"equiv": 0x2261,
"le": 0x2264,
"ge": 0x2265,
"sub": 0x2282,
"sup": 0x2283,
"nsub": 0x2284,
"sube": 0x2286,
"supe": 0x2287,
"oplus": 0x2295,
"otimes": 0x2297,
"perp": 0x22A5,
"sdot": 0x22C5,
"lceil": 0x2308,
"rceil": 0x2309,
"lfloor": 0x230A,
"rfloor": 0x230B,
"lang": 0x2329,
"rang": 0x232A,
"loz": 0x25CA,
"spades": 0x2660,
"clubs": 0x2663,
"hearts": 0x2665,
"diams": 0x2666
});
function createAssignHelper(helperState: EmitHelperState, attributesSegments: Expression[]) {
requestEmitHelper(helperState, assignHelper);
return createCall(
getHelperName(helperState, "__assign"),
/*typeArguments*/ undefined,
attributesSegments
);
}
const assignHelper: EmitHelper = {
name: "typescript:assign",
scoped: false,
priority: 1,
text: `
var __assign = (this && this.__assign) || Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};`
};
}

View File

@@ -13,7 +13,27 @@ namespace ts {
}
if (isExternalModule(node) || compilerOptions.isolatedModules) {
return visitEachChild(node, visitor, context);
const externalHelpersModuleName = getExternalHelpersModuleName(node);
if (externalHelpersModuleName) {
const statements: Statement[] = [];
const statementOffset = addPrologueDirectives(statements, node.statements);
append(statements,
createImportDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
createImportClause(/*name*/ undefined, createNamespaceImport(externalHelpersModuleName)),
createLiteral(externalHelpersModuleNameText)
)
);
addRange(statements, visitNodes(node.statements, visitor, isStatement, statementOffset));
return updateSourceFileNode(
node,
createNodeArray(statements, node.statements));
}
else {
return visitEachChild(node, visitor, context);
}
}
return node;

View File

@@ -82,13 +82,14 @@ namespace ts {
const statements: Statement[] = [];
const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict, sourceElementVisitor);
append(statements, visitNode(currentModuleInfo.externalHelpersImportDeclaration, sourceElementVisitor, isStatement, /*optional*/ true));
addRange(statements, visitNodes(node.statements, sourceElementVisitor, isStatement, statementOffset));
addRange(statements, endLexicalEnvironment());
addExportEqualsIfNeeded(statements, /*emitAsReturn*/ false);
const updated = updateSourceFileNode(node, createNodeArray(statements, node.statements));
if (currentModuleInfo.hasExportStarsToExportValues) {
setEmitFlags(updated, EmitFlags.EmitExportStar | getEmitFlags(node));
addEmitHelper(updated, exportStarHelper);
}
return updated;
@@ -257,6 +258,7 @@ namespace ts {
const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ !compilerOptions.noImplicitUseStrict, sourceElementVisitor);
// Visit each statement of the module body.
append(statements, visitNode(currentModuleInfo.externalHelpersImportDeclaration, sourceElementVisitor, isStatement, /*optional*/ true));
addRange(statements, visitNodes(node.statements, sourceElementVisitor, isStatement, statementOffset));
// End the lexical environment for the module body
@@ -270,7 +272,7 @@ namespace ts {
if (currentModuleInfo.hasExportStarsToExportValues) {
// If we have any `export * from ...` declarations
// we need to inform the emitter to add the __export helper.
setEmitFlags(body, EmitFlags.EmitExportStar);
addEmitHelper(body, exportStarHelper);
}
return body;
@@ -1312,4 +1314,14 @@ namespace ts {
}
}
}
// emit output for the __export helper function
const exportStarHelper: EmitHelper = {
name: "typescript:export-star",
scoped: true,
text: `
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}`
};
}

View File

@@ -82,6 +82,7 @@ namespace ts {
// Add the body of the module.
const dependencyGroups = collectDependencyGroups(moduleInfo.externalImports);
const moduleBodyBlock = createSystemModuleBody(node, dependencyGroups);
const moduleBodyFunction = createFunctionExpression(
/*modifiers*/ undefined,
/*asteriskToken*/ undefined,
@@ -92,7 +93,7 @@ namespace ts {
createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, contextObject)
],
/*type*/ undefined,
createSystemModuleBody(node, dependencyGroups)
moduleBodyBlock
);
// Write the call to `System.register`
@@ -115,7 +116,9 @@ namespace ts {
], node.statements)
);
setEmitFlags(updated, getEmitFlags(node) & ~EmitFlags.EmitEmitHelpers);
if (!(compilerOptions.outFile || compilerOptions.out)) {
moveEmitHelpers(updated, moduleBodyBlock, helper => !helper.scoped);
}
if (noSubstitution) {
noSubstitutionMap[id] = noSubstitution;
@@ -236,6 +239,9 @@ namespace ts {
)
);
// Visit the synthetic external helpers import declaration if present
visitNode(moduleInfo.externalHelpersImportDeclaration, sourceElementVisitor, isStatement, /*optional*/ true);
// Visit the statements of the source file, emitting any transformations into
// the `executeStatements` array. We do this *before* we fill the `setters` array
// as we both emit transformations as well as aggregate some data used when creating
@@ -280,9 +286,7 @@ namespace ts {
)
);
const body = createBlock(statements, /*location*/ undefined, /*multiLine*/ true);
setEmitFlags(body, EmitFlags.EmitEmitHelpers);
return body;
return createBlock(statements, /*location*/ undefined, /*multiLine*/ true);
}
/**
@@ -394,7 +398,13 @@ namespace ts {
if (localNames) {
condition = createLogicalAnd(
condition,
createLogicalNot(createHasOwnProperty(localNames, n))
createLogicalNot(
createCall(
createPropertyAccess(localNames, "hasOwnProperty"),
/*typeArguments*/ undefined,
[n]
)
)
);
}

View File

@@ -48,7 +48,7 @@ namespace ts {
let currentNamespaceContainerName: Identifier;
let currentScope: SourceFile | Block | ModuleBlock | CaseBlock;
let currentScopeFirstDeclarationsOfName: Map<Node>;
let currentExternalHelpersModuleName: Identifier;
let helperState: EmitHelperState;
/**
* Keeps track of whether expression substitution has been enabled for specific edge cases.
@@ -80,7 +80,23 @@ namespace ts {
return node;
}
return visitNode(node, visitor, isSourceFile);
currentSourceFile = node;
currentScope = node;
currentScopeFirstDeclarationsOfName = createMap<Node>();
helperState = { currentSourceFile, compilerOptions };
let visited = visitEachChild(node, sourceElementVisitor, context);
if (compilerOptions.alwaysStrict) {
visited = updateSourceFileNode(visited, ensureUseStrict(visited.statements));
}
addEmitHelpers(visited, helperState.requestedHelpers);
currentSourceFile = undefined;
currentScope = undefined;
currentScopeFirstDeclarationsOfName = undefined;
helperState = undefined;
return visited;
}
/**
@@ -122,10 +138,7 @@ namespace ts {
* @param node The node to visit.
*/
function visitorWorker(node: Node): VisitResult<Node> {
if (node.kind === SyntaxKind.SourceFile) {
return visitSourceFile(<SourceFile>node);
}
else if (node.transformFlags & TransformFlags.TypeScript) {
if (node.transformFlags & TransformFlags.TypeScript) {
// This node is explicitly marked as TypeScript, so we should transform the node.
return visitTypeScript(node);
}
@@ -252,7 +265,6 @@ namespace ts {
return node;
}
/**
* Branching visitor, visits a TypeScript syntax node.
*
@@ -446,7 +458,6 @@ namespace ts {
*/
function onBeforeVisitNode(node: Node) {
switch (node.kind) {
case SyntaxKind.SourceFile:
case SyntaxKind.CaseBlock:
case SyntaxKind.ModuleBlock:
case SyntaxKind.Block:
@@ -465,50 +476,6 @@ namespace ts {
}
}
function visitSourceFile(node: SourceFile) {
currentSourceFile = node;
// ensure "use strict" is emitted in all scenarios in alwaysStrict mode
if (compilerOptions.alwaysStrict) {
node = ensureUseStrict(node);
}
// If the source file requires any helpers and is an external module, and
// the importHelpers compiler option is enabled, emit a synthesized import
// statement for the helpers library.
if (node.flags & NodeFlags.EmitHelperFlags
&& compilerOptions.importHelpers
&& (isExternalModule(node) || compilerOptions.isolatedModules)) {
startLexicalEnvironment();
const statements: Statement[] = [];
const statementOffset = addPrologueDirectives(statements, node.statements, /*ensureUseStrict*/ false, visitor);
const externalHelpersModuleName = createUniqueName(externalHelpersModuleNameText);
const externalHelpersModuleImport = createImportDeclaration(
/*decorators*/ undefined,
/*modifiers*/ undefined,
createImportClause(/*name*/ undefined, createNamespaceImport(externalHelpersModuleName)),
createLiteral(externalHelpersModuleNameText));
externalHelpersModuleImport.parent = node;
externalHelpersModuleImport.flags &= ~NodeFlags.Synthesized;
statements.push(externalHelpersModuleImport);
currentExternalHelpersModuleName = externalHelpersModuleName;
addRange(statements, visitNodes(node.statements, sourceElementVisitor, isStatement, statementOffset));
addRange(statements, endLexicalEnvironment());
currentExternalHelpersModuleName = undefined;
node = updateSourceFileNode(node, createNodeArray(statements, node.statements));
node.externalHelpersModuleName = externalHelpersModuleName;
}
else {
node = visitEachChild(node, sourceElementVisitor, context);
}
setEmitFlags(node, EmitFlags.EmitEmitHelpers | getEmitFlags(node));
return node;
}
/**
* Tests whether we should emit a __decorate call for a class declaration.
*/
@@ -1418,7 +1385,7 @@ namespace ts {
: undefined;
const helper = createDecorateHelper(
currentExternalHelpersModuleName,
helperState,
decoratorExpressions,
prefix,
memberName,
@@ -1456,7 +1423,7 @@ namespace ts {
const classAlias = classAliases && classAliases[getOriginalNodeId(node)];
const localName = getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true);
const decorate = createDecorateHelper(currentExternalHelpersModuleName, decoratorExpressions, localName);
const decorate = createDecorateHelper(helperState, decoratorExpressions, localName);
const expression = createAssignment(localName, classAlias ? createAssignment(classAlias, decorate) : decorate);
setEmitFlags(expression, EmitFlags.NoComments);
setSourceMapRange(expression, moveRangePastDecorators(node));
@@ -1484,7 +1451,7 @@ namespace ts {
expressions = [];
for (const decorator of decorators) {
const helper = createParamHelper(
currentExternalHelpersModuleName,
helperState,
transformDecorator(decorator),
parameterOffset,
/*location*/ decorator.expression);
@@ -1514,13 +1481,13 @@ namespace ts {
function addOldTypeMetadata(node: Declaration, decoratorExpressions: Expression[]) {
if (compilerOptions.emitDecoratorMetadata) {
if (shouldAddTypeMetadata(node)) {
decoratorExpressions.push(createMetadataHelper(currentExternalHelpersModuleName, "design:type", serializeTypeOfNode(node)));
decoratorExpressions.push(createMetadataHelper(helperState, "design:type", serializeTypeOfNode(node)));
}
if (shouldAddParamTypesMetadata(node)) {
decoratorExpressions.push(createMetadataHelper(currentExternalHelpersModuleName, "design:paramtypes", serializeParameterTypesOfNode(node)));
decoratorExpressions.push(createMetadataHelper(helperState, "design:paramtypes", serializeParameterTypesOfNode(node)));
}
if (shouldAddReturnTypeMetadata(node)) {
decoratorExpressions.push(createMetadataHelper(currentExternalHelpersModuleName, "design:returntype", serializeReturnTypeOfNode(node)));
decoratorExpressions.push(createMetadataHelper(helperState, "design:returntype", serializeReturnTypeOfNode(node)));
}
}
}
@@ -1538,7 +1505,7 @@ namespace ts {
(properties || (properties = [])).push(createPropertyAssignment("returnType", createArrowFunction(/*modifiers*/ undefined, /*typeParameters*/ undefined, [], /*type*/ undefined, createToken(SyntaxKind.EqualsGreaterThanToken), serializeReturnTypeOfNode(node))));
}
if (properties) {
decoratorExpressions.push(createMetadataHelper(currentExternalHelpersModuleName, "design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true)));
decoratorExpressions.push(createMetadataHelper(helperState, "design:typeinfo", createObjectLiteral(properties, /*location*/ undefined, /*multiLine*/ true)));
}
}
}
@@ -3404,4 +3371,77 @@ namespace ts {
: undefined;
}
}
function createParamHelper(helperState: EmitHelperState, expression: Expression, parameterOffset: number, location?: TextRange) {
requestEmitHelper(helperState, paramHelper);
return createCall(
getHelperName(helperState, "__param"),
/*typeArguments*/ undefined,
[
createLiteral(parameterOffset),
expression
],
location
);
}
function createMetadataHelper(helperState: EmitHelperState, metadataKey: string, metadataValue: Expression) {
requestEmitHelper(helperState, metadataHelper);
return createCall(
getHelperName(helperState, "__metadata"),
/*typeArguments*/ undefined,
[
createLiteral(metadataKey),
metadataValue
]
);
}
function createDecorateHelper(helperState: EmitHelperState, decoratorExpressions: Expression[], target: Expression, memberName?: Expression, descriptor?: Expression, location?: TextRange) {
const argumentsArray: Expression[] = [];
argumentsArray.push(createArrayLiteral(decoratorExpressions, /*location*/ undefined, /*multiLine*/ true));
argumentsArray.push(target);
if (memberName) {
argumentsArray.push(memberName);
if (descriptor) {
argumentsArray.push(descriptor);
}
}
requestEmitHelper(helperState, decorateHelper);
return createCall(getHelperName(helperState, "__decorate"), /*typeArguments*/ undefined, argumentsArray, location);
}
const decorateHelper: EmitHelper = {
name: "typescript:decorate",
scoped: false,
priority: 2,
text: `
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};`
};
const metadataHelper: EmitHelper = {
name: "typescript:metadata",
scoped: false,
priority: 3,
text: `
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};`
};
const paramHelper: EmitHelper = {
name: "typescript:param",
scoped: false,
priority: 4,
text: `
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};`
};
}

View File

@@ -2088,8 +2088,6 @@ namespace ts {
/* @internal */ imports: LiteralExpression[];
/* @internal */ moduleAugmentations: LiteralExpression[];
/* @internal */ patternAmbientModules?: PatternAmbientModule[];
// The synthesized identifier for an imported external helpers module.
/* @internal */ externalHelpersModuleName?: Identifier;
}
export interface ScriptReferenceHost {
@@ -3476,20 +3474,18 @@ namespace ts {
/* @internal */
export interface EmitNode {
flags?: EmitFlags;
commentRange?: TextRange;
sourceMapRange?: TextRange;
tokenSourceMapRanges?: Map<TextRange>;
annotatedNodes?: Node[]; // Tracks Parse-tree nodes with EmitNodes for eventual cleanup.
constantValue?: number;
flags?: EmitFlags; // Flags that customize emit
commentRange?: TextRange; // The text range to use when emitting leading or trailing comments
sourceMapRange?: TextRange; // The text range to use when emitting leading or trailing source mappings
tokenSourceMapRanges?: Map<TextRange>; // The text range to use when emitting source mappings for tokens
constantValue?: number; // The constant value of an expression
externalHelpersModuleName?: Identifier; // The local name for an imported helpers module
helpers?: EmitHelper[]; // Emit helpers for the node
}
/* @internal */
export const enum EmitFlags {
EmitEmitHelpers = 1 << 0, // Any emit helpers should be written to this node.
EmitExportStar = 1 << 1, // The export * helper should be written to this node.
EmitSuperHelper = 1 << 2, // Emit the basic _super helper for async methods.
EmitAdvancedSuperHelper = 1 << 3, // Emit the advanced _super helper for async methods.
UMDDefine = 1 << 4, // This node should be replaced with the UMD define helper.
SingleLine = 1 << 5, // The contents of this node should be emitted on a single line.
AdviseOnEmitNode = 1 << 6, // The printer should invoke the onEmitNode callback when printing this node.
@@ -3517,6 +3513,14 @@ namespace ts {
HasEndOfDeclarationMarker = 1 << 25, // Declaration has an associated NotEmittedStatement to mark the end of the declaration
}
/* @internal */
export interface EmitHelper {
readonly name: string; // A unique name for this helper.
readonly scoped: boolean; // Indicates whether ther helper MUST be emitted in the current scope.
readonly text: string; // ES3-compatible raw script text.
readonly priority?: number; // Helpers with a higher priority are emitted earlier than other helpers on the node.
}
/* @internal */
export const enum EmitContext {
SourceFile, // Emitting a SourceFile

View File

@@ -3493,153 +3493,6 @@ namespace ts {
return positionIsSynthesized(range.pos) ? -1 : skipTrivia(sourceFile.text, range.pos);
}
export interface ExternalModuleInfo {
externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; // imports of other external modules
exportSpecifiers: Map<ExportSpecifier[]>; // export specifiers by name
exportedBindings: Map<Identifier[]>; // exported names of local declarations
exportedNames: Identifier[]; // all exported names local to module
exportEquals: ExportAssignment | undefined; // an export= declaration if one was present
hasExportStarsToExportValues: boolean; // whether this module contains export*
}
export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver): ExternalModuleInfo {
const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = [];
const exportSpecifiers = createMap<ExportSpecifier[]>();
const exportedBindings = createMap<Identifier[]>();
const uniqueExports = createMap<Identifier>();
let hasExportDefault = false;
let exportEquals: ExportAssignment = undefined;
let hasExportStarsToExportValues = false;
for (const node of sourceFile.statements) {
switch (node.kind) {
case SyntaxKind.ImportDeclaration:
// import "mod"
// import x from "mod"
// import * as x from "mod"
// import { x, y } from "mod"
externalImports.push(<ImportDeclaration>node);
break;
case SyntaxKind.ImportEqualsDeclaration:
if ((<ImportEqualsDeclaration>node).moduleReference.kind === SyntaxKind.ExternalModuleReference) {
// import x = require("mod")
externalImports.push(<ImportEqualsDeclaration>node);
}
break;
case SyntaxKind.ExportDeclaration:
if ((<ExportDeclaration>node).moduleSpecifier) {
if (!(<ExportDeclaration>node).exportClause) {
// export * from "mod"
externalImports.push(<ExportDeclaration>node);
hasExportStarsToExportValues = true;
}
else {
// export { x, y } from "mod"
externalImports.push(<ExportDeclaration>node);
}
}
else {
// export { x, y }
for (const specifier of (<ExportDeclaration>node).exportClause.elements) {
if (!uniqueExports[specifier.name.text]) {
const name = specifier.propertyName || specifier.name;
multiMapAdd(exportSpecifiers, name.text, specifier);
const decl = resolver.getReferencedImportDeclaration(name)
|| resolver.getReferencedValueDeclaration(name);
if (decl) {
multiMapAdd(exportedBindings, getOriginalNodeId(decl), specifier.name);
}
uniqueExports[specifier.name.text] = specifier.name;
}
}
}
break;
case SyntaxKind.ExportAssignment:
if ((<ExportAssignment>node).isExportEquals && !exportEquals) {
// export = x
exportEquals = <ExportAssignment>node;
}
break;
case SyntaxKind.VariableStatement:
if (hasModifier(node, ModifierFlags.Export)) {
for (const decl of (<VariableStatement>node).declarationList.declarations) {
collectExportedVariableInfo(decl, uniqueExports);
}
}
break;
case SyntaxKind.FunctionDeclaration:
if (hasModifier(node, ModifierFlags.Export)) {
if (hasModifier(node, ModifierFlags.Default)) {
// export default function() { }
if (!hasExportDefault) {
multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(<FunctionDeclaration>node));
hasExportDefault = true;
}
}
else {
// export function x() { }
const name = (<FunctionDeclaration>node).name;
if (!uniqueExports[name.text]) {
multiMapAdd(exportedBindings, getOriginalNodeId(node), name);
uniqueExports[name.text] = name;
}
}
}
break;
case SyntaxKind.ClassDeclaration:
if (hasModifier(node, ModifierFlags.Export)) {
if (hasModifier(node, ModifierFlags.Default)) {
// export default class { }
if (!hasExportDefault) {
multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(<ClassDeclaration>node));
hasExportDefault = true;
}
}
else {
// export class x { }
const name = (<ClassDeclaration>node).name;
if (!uniqueExports[name.text]) {
multiMapAdd(exportedBindings, getOriginalNodeId(node), name);
uniqueExports[name.text] = name;
}
}
}
break;
}
}
let exportedNames: Identifier[];
for (const key in uniqueExports) {
exportedNames = ts.append(exportedNames, uniqueExports[key]);
}
return { externalImports, exportSpecifiers, exportEquals, hasExportStarsToExportValues, exportedBindings, exportedNames };
}
function collectExportedVariableInfo(decl: VariableDeclaration | BindingElement, uniqueExports: Map<Identifier>) {
if (isBindingPattern(decl.name)) {
for (const element of decl.name.elements) {
if (!isOmittedExpression(element)) {
collectExportedVariableInfo(element, uniqueExports);
}
}
}
else if (!isGeneratedIdentifier(decl.name)) {
if (!uniqueExports[decl.name.text]) {
uniqueExports[decl.name.text] = decl.name;
}
}
}
/**
* Determines whether a name was originally the declaration name of an enum or namespace
* declaration.