Skip to content

Commit 2b8ca38

Browse files
committed
Merge branch 'master' into fix39947
2 parents dcfce3f + 90e944d commit 2b8ca38

28 files changed

+589
-88
lines changed

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ gulp LKG # Replace the last known good with the built one.
7878
# Bootstrapping step to be executed when the built compiler reaches a stable state.
7979
gulp tests # Build the test infrastructure using the built compiler.
8080
gulp runtests # Run tests using the built compiler and test infrastructure.
81-
# Some low-value tests are skipped when not on a CI machine - you can use the
82-
# --skipPercent=0 command to override this behavior and run all tests locally.
8381
# You can override the specific suite runner used or specify a test for this command.
8482
# Use --tests=<testPath> for a specific test and/or --runner=<runnerName> for a specific suite.
8583
# Valid runners include conformance, compiler, fourslash, project, user, and docker

package-lock.json

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/compiler/checker.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6783,6 +6783,38 @@ namespace ts {
67836783
const targetName = getInternalSymbolName(target, verbatimTargetName);
67846784
includePrivateSymbol(target); // the target may be within the same scope - attempt to serialize it first
67856785
switch (node.kind) {
6786+
case SyntaxKind.BindingElement:
6787+
if (node.parent?.parent?.kind === SyntaxKind.VariableDeclaration) {
6788+
// const { SomeClass } = require('./lib');
6789+
const specifier = getSpecifierForModuleSymbol(target.parent || target, context); // './lib'
6790+
const { propertyName } = node as BindingElement;
6791+
addResult(factory.createImportDeclaration(
6792+
/*decorators*/ undefined,
6793+
/*modifiers*/ undefined,
6794+
factory.createImportClause(/*isTypeOnly*/ false, /*name*/ undefined, factory.createNamedImports([factory.createImportSpecifier(
6795+
propertyName && isIdentifier(propertyName) ? factory.createIdentifier(idText(propertyName)) : undefined,
6796+
factory.createIdentifier(localName)
6797+
)])),
6798+
factory.createStringLiteral(specifier)
6799+
), ModifierFlags.None);
6800+
break;
6801+
}
6802+
// At present, the below case should be entirely unhit, as, generally speaking, the below case is *usually* bound
6803+
// such that the `BinaryExpression` is the declaration rather than the specific, nested binding element
6804+
// (because we don't seek to emit an alias in these forms yet). As such, the `BinaryExpression` switch case
6805+
// will be what actually handles this form. _However_, in anticipation of binding the below with proper
6806+
// alias symbols, I'm _pretty comfortable_ including the case here, even though it is not yet live.
6807+
if (node.parent?.parent?.kind === SyntaxKind.BinaryExpression) {
6808+
// module.exports = { SomeClass }
6809+
serializeExportSpecifier(
6810+
unescapeLeadingUnderscores(symbol.escapedName),
6811+
targetName
6812+
);
6813+
break;
6814+
}
6815+
// We don't know how to serialize this (nested?) binding element
6816+
Debug.failBadSyntaxKind(node.parent?.parent || node, "Unhandled binding element grandparent kind in declaration serialization");
6817+
break;
67866818
case SyntaxKind.VariableDeclaration:
67876819
// commonjs require: const x = require('y')
67886820
if (isPropertyAccessExpression((node as VariableDeclaration).initializer!)) {

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4719,6 +4719,10 @@
47194719
"code": 6385,
47204720
"reportsDeprecated": true
47214721
},
4722+
"Performance timings for '--diagnostics' or '--extendedDiagnostics' are not available in this session. A native implementation of the Web Performance API could not be found.": {
4723+
"category": "Message",
4724+
"code": 6386
4725+
},
47224726

47234727
"The expected type comes from property '{0}' which is declared here on type '{1}'": {
47244728
"category": "Message",

src/compiler/emitter.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5136,7 +5136,12 @@ namespace ts {
51365136
hasWrittenComment = false;
51375137

51385138
if (isEmittedNode) {
5139-
forEachLeadingCommentToEmit(pos, emitLeadingComment);
5139+
if (pos === 0 && currentSourceFile?.isDeclarationFile) {
5140+
forEachLeadingCommentToEmit(pos, emitNonTripleSlashLeadingComment);
5141+
}
5142+
else {
5143+
forEachLeadingCommentToEmit(pos, emitLeadingComment);
5144+
}
51405145
}
51415146
else if (pos === 0) {
51425147
// If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node,
@@ -5157,6 +5162,12 @@ namespace ts {
51575162
}
51585163
}
51595164

5165+
function emitNonTripleSlashLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
5166+
if (!isTripleSlashComment(commentPos, commentEnd)) {
5167+
emitLeadingComment(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos);
5168+
}
5169+
}
5170+
51605171
function shouldWriteComment(text: string, pos: number) {
51615172
if (printerOptions.onlyPrintJsDocStyle) {
51625173
return (isJSDocLikeText(text, pos) || isPinnedComment(text, pos));

src/compiler/factory/nodeTests.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,10 @@ namespace ts {
237237
return node.kind === SyntaxKind.TemplateLiteralTypeSpan;
238238
}
239239

240+
export function isTemplateLiteralTypeNode(node: Node): node is TemplateLiteralTypeNode {
241+
return node.kind === SyntaxKind.TemplateLiteralType;
242+
}
243+
240244
// Binding patterns
241245

242246
export function isObjectBindingPattern(node: Node): node is ObjectBindingPattern {

src/compiler/performance.ts

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
11
/*@internal*/
22
/** Performance measurements for the compiler. */
33
namespace ts.performance {
4-
declare const onProfilerEvent: { (markName: string): void; profiler: boolean; };
5-
6-
// NOTE: cannot use ts.noop as core.ts loads after this
7-
const profilerEvent: (markName: string) => void = typeof onProfilerEvent === "function" && onProfilerEvent.profiler === true ? onProfilerEvent : () => { /*empty*/ };
8-
9-
let enabled = false;
10-
let profilerStart = 0;
11-
let counts: ESMap<string, number>;
12-
let marks: ESMap<string, number>;
13-
let measures: ESMap<string, number>;
4+
let perfHooks: PerformanceHooks | undefined;
5+
let perfObserver: PerformanceObserver | undefined;
6+
// when set, indicates the implementation of `Performance` to use for user timing.
7+
// when unset, indicates user timing is unavailable or disabled.
8+
let performanceImpl: Performance | undefined;
149

1510
export interface Timer {
1611
enter(): void;
@@ -46,18 +41,16 @@ namespace ts.performance {
4641
}
4742

4843
export const nullTimer: Timer = { enter: noop, exit: noop };
44+
const counts = new Map<string, number>();
45+
const durations = new Map<string, number>();
4946

5047
/**
5148
* Marks a performance event.
5249
*
5350
* @param markName The name of the mark.
5451
*/
5552
export function mark(markName: string) {
56-
if (enabled) {
57-
marks.set(markName, timestamp());
58-
counts.set(markName, (counts.get(markName) || 0) + 1);
59-
profilerEvent(markName);
60-
}
53+
performanceImpl?.mark(markName);
6154
}
6255

6356
/**
@@ -70,11 +63,7 @@ namespace ts.performance {
7063
* used.
7164
*/
7265
export function measure(measureName: string, startMarkName?: string, endMarkName?: string) {
73-
if (enabled) {
74-
const end = endMarkName && marks.get(endMarkName) || timestamp();
75-
const start = startMarkName && marks.get(startMarkName) || profilerStart;
76-
measures.set(measureName, (measures.get(measureName) || 0) + (end - start));
77-
}
66+
performanceImpl?.measure(measureName, startMarkName, endMarkName);
7867
}
7968

8069
/**
@@ -83,7 +72,7 @@ namespace ts.performance {
8372
* @param markName The name of the mark.
8473
*/
8574
export function getCount(markName: string) {
86-
return counts && counts.get(markName) || 0;
75+
return counts.get(markName) || 0;
8776
}
8877

8978
/**
@@ -92,7 +81,7 @@ namespace ts.performance {
9281
* @param measureName The name of the measure whose durations should be accumulated.
9382
*/
9483
export function getDuration(measureName: string) {
95-
return measures && measures.get(measureName) || 0;
84+
return durations.get(measureName) || 0;
9685
}
9786

9887
/**
@@ -101,22 +90,42 @@ namespace ts.performance {
10190
* @param cb The action to perform for each measure
10291
*/
10392
export function forEachMeasure(cb: (measureName: string, duration: number) => void) {
104-
measures.forEach((measure, key) => {
105-
cb(key, measure);
106-
});
93+
durations.forEach((duration, measureName) => cb(measureName, duration));
94+
}
95+
96+
/**
97+
* Indicates whether the performance API is enabled.
98+
*/
99+
export function isEnabled() {
100+
return !!performanceImpl;
107101
}
108102

109103
/** Enables (and resets) performance measurements for the compiler. */
110104
export function enable() {
111-
counts = new Map<string, number>();
112-
marks = new Map<string, number>();
113-
measures = new Map<string, number>();
114-
enabled = true;
115-
profilerStart = timestamp();
105+
if (!performanceImpl) {
106+
perfHooks ||= tryGetNativePerformanceHooks();
107+
if (!perfHooks) return false;
108+
perfObserver ||= new perfHooks.PerformanceObserver(updateStatisticsFromList);
109+
perfObserver.observe({ entryTypes: ["mark", "measure"] });
110+
performanceImpl = perfHooks.performance;
111+
}
112+
return true;
116113
}
117114

118115
/** Disables performance measurements for the compiler. */
119116
export function disable() {
120-
enabled = false;
117+
perfObserver?.disconnect();
118+
performanceImpl = undefined;
119+
counts.clear();
120+
durations.clear();
121+
}
122+
123+
function updateStatisticsFromList(list: PerformanceObserverEntryList) {
124+
for (const mark of list.getEntriesByType("mark")) {
125+
counts.set(mark.name, (counts.get(mark.name) || 0) + 1);
126+
}
127+
for (const measure of list.getEntriesByType("measure")) {
128+
durations.set(measure.name, (durations.get(measure.name) || 0) + measure.duration);
129+
}
121130
}
122131
}

src/compiler/performanceCore.ts

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*@internal*/
2+
namespace ts {
3+
// The following definitions provide the minimum compatible support for the Web Performance User Timings API
4+
// between browsers and NodeJS:
5+
6+
export interface PerformanceHooks {
7+
performance: Performance;
8+
PerformanceObserver: PerformanceObserverConstructor;
9+
}
10+
11+
export interface Performance {
12+
mark(name: string): void;
13+
measure(name: string, startMark?: string, endMark?: string): void;
14+
now(): number;
15+
timeOrigin: number;
16+
}
17+
18+
export interface PerformanceEntry {
19+
name: string;
20+
entryType: string;
21+
startTime: number;
22+
duration: number;
23+
}
24+
25+
export interface PerformanceObserverEntryList {
26+
getEntries(): PerformanceEntryList;
27+
getEntriesByName(name: string, type?: string): PerformanceEntryList;
28+
getEntriesByType(type: string): PerformanceEntryList;
29+
}
30+
31+
export interface PerformanceObserver {
32+
disconnect(): void;
33+
observe(options: { entryTypes: readonly string[] }): void;
34+
}
35+
36+
export type PerformanceObserverConstructor = new (callback: (list: PerformanceObserverEntryList, observer: PerformanceObserver) => void) => PerformanceObserver;
37+
export type PerformanceEntryList = PerformanceEntry[];
38+
39+
// Browser globals for the Web Performance User Timings API
40+
declare const performance: Performance | undefined;
41+
declare const PerformanceObserver: PerformanceObserverConstructor | undefined;
42+
43+
// eslint-disable-next-line @typescript-eslint/naming-convention
44+
function hasRequiredAPI(performance: Performance | undefined, PerformanceObserver: PerformanceObserverConstructor | undefined) {
45+
return typeof performance === "object" &&
46+
typeof performance.timeOrigin === "number" &&
47+
typeof performance.mark === "function" &&
48+
typeof performance.measure === "function" &&
49+
typeof performance.now === "function" &&
50+
typeof PerformanceObserver === "function";
51+
}
52+
53+
function tryGetWebPerformanceHooks(): PerformanceHooks | undefined {
54+
if (typeof performance === "object" &&
55+
typeof PerformanceObserver === "function" &&
56+
hasRequiredAPI(performance, PerformanceObserver)) {
57+
return {
58+
performance,
59+
PerformanceObserver
60+
};
61+
}
62+
}
63+
64+
function tryGetNodePerformanceHooks(): PerformanceHooks | undefined {
65+
if (typeof module === "object" && typeof require === "function") {
66+
try {
67+
const { performance, PerformanceObserver } = require("perf_hooks") as typeof import("perf_hooks");
68+
if (hasRequiredAPI(performance, PerformanceObserver)) {
69+
// There is a bug in Node's performance.measure prior to 12.16.3/13.13.0 that does not
70+
// match the Web Performance API specification. Node's implementation did not allow
71+
// optional `start` and `end` arguments for `performance.measure`.
72+
// See https://github.com/nodejs/node/pull/32651 for more information.
73+
const version = new Version(process.versions.node);
74+
const range = new VersionRange("<12.16.3 || 13 <13.13");
75+
if (range.test(version)) {
76+
return {
77+
performance: {
78+
get timeOrigin() { return performance.timeOrigin; },
79+
now() { return performance.now(); },
80+
mark(name) { return performance.mark(name); },
81+
measure(name, start = "nodeStart", end?) {
82+
if (end === undefined) {
83+
end = "__performance.measure-fix__";
84+
performance.mark(end);
85+
}
86+
performance.measure(name, start, end);
87+
if (end === "__performance.measure-fix__") {
88+
performance.clearMarks("__performance.measure-fix__");
89+
}
90+
}
91+
},
92+
PerformanceObserver
93+
};
94+
}
95+
return {
96+
performance,
97+
PerformanceObserver
98+
};
99+
}
100+
}
101+
catch {
102+
// ignore errors
103+
}
104+
}
105+
}
106+
107+
// Unlike with the native Map/Set 'tryGet' functions in corePublic.ts, we eagerly evaluate these
108+
// since we will need them for `timestamp`, below.
109+
const nativePerformanceHooks = tryGetWebPerformanceHooks() || tryGetNodePerformanceHooks();
110+
const nativePerformance = nativePerformanceHooks?.performance;
111+
112+
export function tryGetNativePerformanceHooks() {
113+
return nativePerformanceHooks;
114+
}
115+
116+
/** Gets a timestamp with (at least) ms resolution */
117+
export const timestamp =
118+
nativePerformance ? () => nativePerformance.now() :
119+
Date.now ? Date.now :
120+
() => +(new Date());
121+
}

0 commit comments

Comments
 (0)