@openrewrite/rewrite 8.62.0 → 8.62.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/java/rpc.d.ts +3 -3
- package/dist/java/rpc.d.ts.map +1 -1
- package/dist/java/rpc.js +6 -6
- package/dist/java/rpc.js.map +1 -1
- package/dist/java/tree.d.ts +31 -33
- package/dist/java/tree.d.ts.map +1 -1
- package/dist/java/tree.js +125 -7
- package/dist/java/tree.js.map +1 -1
- package/dist/java/type.d.ts +70 -57
- package/dist/java/type.d.ts.map +1 -1
- package/dist/java/type.js +454 -25
- package/dist/java/type.js.map +1 -1
- package/dist/java/visitor.d.ts +4 -4
- package/dist/java/visitor.d.ts.map +1 -1
- package/dist/java/visitor.js +34 -28
- package/dist/java/visitor.js.map +1 -1
- package/dist/javascript/assertions.d.ts +3 -0
- package/dist/javascript/assertions.d.ts.map +1 -1
- package/dist/javascript/assertions.js +78 -1
- package/dist/javascript/assertions.js.map +1 -1
- package/dist/javascript/format.js +4 -4
- package/dist/javascript/format.js.map +1 -1
- package/dist/javascript/parser.d.ts +1 -1
- package/dist/javascript/parser.d.ts.map +1 -1
- package/dist/javascript/parser.js +38 -11
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/rpc.js +1 -4
- package/dist/javascript/rpc.js.map +1 -1
- package/dist/javascript/tree.d.ts +33 -35
- package/dist/javascript/tree.d.ts.map +1 -1
- package/dist/javascript/tree.js +70 -17
- package/dist/javascript/tree.js.map +1 -1
- package/dist/javascript/type-mapping.d.ts +44 -8
- package/dist/javascript/type-mapping.d.ts.map +1 -1
- package/dist/javascript/type-mapping.js +571 -101
- package/dist/javascript/type-mapping.js.map +1 -1
- package/dist/javascript/visitor.d.ts +2 -2
- package/dist/javascript/visitor.d.ts.map +1 -1
- package/dist/javascript/visitor.js.map +1 -1
- package/dist/json/tree.d.ts +0 -2
- package/dist/json/tree.d.ts.map +1 -1
- package/dist/json/tree.js +12 -2
- package/dist/json/tree.js.map +1 -1
- package/dist/reference.js +1 -1
- package/dist/reference.js.map +1 -1
- package/dist/rpc/chrome-profiler.d.ts +25 -0
- package/dist/rpc/chrome-profiler.d.ts.map +1 -0
- package/dist/rpc/chrome-profiler.js +405 -0
- package/dist/rpc/chrome-profiler.js.map +1 -0
- package/dist/rpc/queue.d.ts +0 -3
- package/dist/rpc/queue.d.ts.map +1 -1
- package/dist/rpc/queue.js +6 -6
- package/dist/rpc/queue.js.map +1 -1
- package/dist/rpc/request/parse.d.ts.map +1 -1
- package/dist/rpc/request/parse.js.map +1 -1
- package/dist/rpc/rewrite-rpc.d.ts +1 -0
- package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
- package/dist/rpc/rewrite-rpc.js +1 -1
- package/dist/rpc/rewrite-rpc.js.map +1 -1
- package/dist/rpc/server.d.ts.map +1 -1
- package/dist/rpc/server.js +26 -1
- package/dist/rpc/server.js.map +1 -1
- package/dist/test/rewrite-test.d.ts +1 -1
- package/dist/test/rewrite-test.d.ts.map +1 -1
- package/dist/test/rewrite-test.js +18 -2
- package/dist/test/rewrite-test.js.map +1 -1
- package/dist/text/tree.d.ts +0 -2
- package/dist/text/tree.d.ts.map +1 -1
- package/dist/text/tree.js +4 -17
- package/dist/text/tree.js.map +1 -1
- package/dist/tree.d.ts +1 -0
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +14 -0
- package/dist/tree.js.map +1 -1
- package/dist/version.txt +1 -1
- package/package.json +3 -3
- package/src/java/rpc.ts +18 -15
- package/src/java/tree.ts +68 -38
- package/src/java/type.ts +475 -74
- package/src/java/visitor.ts +45 -39
- package/src/javascript/assertions.ts +52 -1
- package/src/javascript/format.ts +4 -4
- package/src/javascript/parser.ts +51 -18
- package/src/javascript/rpc.ts +8 -10
- package/src/javascript/tree.ts +34 -35
- package/src/javascript/type-mapping.ts +582 -47
- package/src/javascript/visitor.ts +5 -5
- package/src/json/tree.ts +1 -2
- package/src/reference.ts +1 -1
- package/src/rpc/chrome-profiler.ts +373 -0
- package/src/rpc/queue.ts +8 -12
- package/src/rpc/request/parse.ts +1 -1
- package/src/rpc/rewrite-rpc.ts +3 -2
- package/src/rpc/server.ts +30 -1
- package/src/test/rewrite-test.ts +19 -3
- package/src/text/tree.ts +0 -1
- package/src/tree.ts +15 -0
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import {mapAsync} from "../util";
|
|
19
19
|
import {SourceFile} from "../tree";
|
|
20
20
|
import {ValidImmerRecipeReturnType} from "../visitor";
|
|
21
|
-
import {Expression, J,
|
|
21
|
+
import {Expression, J, Type, JavaVisitor, NameTree, Statement, TypedTree} from "../java";
|
|
22
22
|
import {createDraft, Draft, finishDraft} from "immer";
|
|
23
23
|
import {isJavaScript, JS, JSX} from "./tree";
|
|
24
24
|
import ComputedPropertyName = JS.ComputedPropertyName;
|
|
@@ -45,7 +45,7 @@ export class JavaScriptVisitor<P> extends JavaVisitor<P> {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// noinspection JSUnusedLocalSymbols
|
|
48
|
-
protected override async visitType(javaType:
|
|
48
|
+
protected override async visitType(javaType: Type | undefined, p: P): Promise<Type | undefined> {
|
|
49
49
|
return javaType;
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -254,7 +254,7 @@ export class JavaScriptVisitor<P> extends JavaVisitor<P> {
|
|
|
254
254
|
draft.function = await this.visitOptionalRightPadded(functionCall.function, p);
|
|
255
255
|
draft.typeParameters = await this.visitOptionalContainer(functionCall.typeParameters, p);
|
|
256
256
|
draft.arguments = await this.visitContainer(functionCall.arguments, p);
|
|
257
|
-
draft.functionType = await this.visitType(functionCall.functionType, p) as
|
|
257
|
+
draft.functionType = await this.visitType(functionCall.functionType, p) as Type.Method | undefined;
|
|
258
258
|
});
|
|
259
259
|
}
|
|
260
260
|
|
|
@@ -785,7 +785,7 @@ export class JavaScriptVisitor<P> extends JavaVisitor<P> {
|
|
|
785
785
|
draft.name = await this.visitDefined<ComputedPropertyName>(computedPropMethod.name, p);
|
|
786
786
|
draft.parameters = await this.visitContainer(computedPropMethod.parameters, p);
|
|
787
787
|
draft.body = computedPropMethod.body && await this.visitDefined<J.Block>(computedPropMethod.body, p);
|
|
788
|
-
draft.methodType = computedPropMethod.methodType && (await this.visitType(computedPropMethod.methodType, p) as
|
|
788
|
+
draft.methodType = computedPropMethod.methodType && (await this.visitType(computedPropMethod.methodType, p) as Type.Method);
|
|
789
789
|
});
|
|
790
790
|
}
|
|
791
791
|
|
|
@@ -842,7 +842,7 @@ export class JavaScriptVisitor<P> extends JavaVisitor<P> {
|
|
|
842
842
|
draft.propertyName = bindingElement.propertyName && await this.visitRightPadded(bindingElement.propertyName, p);
|
|
843
843
|
draft.name = await this.visitDefined<TypedTree>(bindingElement.name, p);
|
|
844
844
|
draft.initializer = bindingElement.initializer && await this.visitLeftPadded(bindingElement.initializer, p);
|
|
845
|
-
draft.variableType = bindingElement.variableType && (await this.visitType(bindingElement.variableType, p) as
|
|
845
|
+
draft.variableType = bindingElement.variableType && (await this.visitType(bindingElement.variableType, p) as Type.Variable);
|
|
846
846
|
});
|
|
847
847
|
}
|
|
848
848
|
|
package/src/json/tree.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import {Markers} from "../markers";
|
|
17
|
-
import {SourceFile, Tree
|
|
17
|
+
import {SourceFile, Tree} from "../tree";
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
export interface Json extends Tree {
|
|
@@ -23,7 +23,6 @@ export interface Json extends Tree {
|
|
|
23
23
|
|
|
24
24
|
export namespace Json {
|
|
25
25
|
export const Kind = {
|
|
26
|
-
...TreeKind,
|
|
27
26
|
Array: "org.openrewrite.json.tree.Json$Array",
|
|
28
27
|
Document: "org.openrewrite.json.tree.Json$Document",
|
|
29
28
|
Empty: "org.openrewrite.json.tree.Json$Empty",
|
package/src/reference.ts
CHANGED
|
@@ -44,7 +44,7 @@ export function asRef<T extends {} | undefined>(obj: T): T extends undefined ? u
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
export function isRef(obj: any): obj is Reference {
|
|
47
|
-
return obj !== undefined && obj[REFERENCE_KEY] === true;
|
|
47
|
+
return obj !== undefined && obj !== null && obj[REFERENCE_KEY] === true;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export class ReferenceMap {
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 the original author or authors.
|
|
3
|
+
* <p>
|
|
4
|
+
* Licensed under the Moderne Source Available License (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
* <p>
|
|
8
|
+
* https://docs.moderne.io/licensing/moderne-source-available-license
|
|
9
|
+
* <p>
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import * as fs from 'fs';
|
|
17
|
+
import * as path from 'path';
|
|
18
|
+
import * as v8 from 'v8';
|
|
19
|
+
import * as inspector from 'inspector';
|
|
20
|
+
|
|
21
|
+
export class ChromeProfiler {
|
|
22
|
+
private traceEvents: any[] = [];
|
|
23
|
+
private memoryInterval?: NodeJS.Timeout;
|
|
24
|
+
private saveInterval?: NodeJS.Timeout;
|
|
25
|
+
private session?: inspector.Session;
|
|
26
|
+
private readonly pid = process.pid;
|
|
27
|
+
private readonly tid = 1; // Main thread
|
|
28
|
+
private readonly tracePath: string;
|
|
29
|
+
private exitHandlersRegistered = false;
|
|
30
|
+
private startTime: number = 0;
|
|
31
|
+
private ttsCounter: number = 0;
|
|
32
|
+
private lastProfileTime: number = 0;
|
|
33
|
+
private profileNodes = new Map();
|
|
34
|
+
|
|
35
|
+
constructor(outputPath?: string) {
|
|
36
|
+
this.tracePath = outputPath || path.join(process.cwd(), 'chrome-trace.json');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async start() {
|
|
40
|
+
this.startTime = Date.now() * 1000; // Convert to microseconds
|
|
41
|
+
this.lastProfileTime = this.startTime;
|
|
42
|
+
|
|
43
|
+
// Add initial metadata events
|
|
44
|
+
this.addMetadataEvents();
|
|
45
|
+
|
|
46
|
+
// Start V8 Inspector session for CPU profiling
|
|
47
|
+
this.session = new inspector.Session();
|
|
48
|
+
this.session.connect();
|
|
49
|
+
|
|
50
|
+
// Enable and start CPU profiling
|
|
51
|
+
await this.enableCpuProfiling();
|
|
52
|
+
|
|
53
|
+
// Start collecting memory data
|
|
54
|
+
this.startMemoryTracking();
|
|
55
|
+
|
|
56
|
+
// Save trace periodically and collect CPU profile samples
|
|
57
|
+
this.saveInterval = setInterval(async () => {
|
|
58
|
+
await this.collectCpuProfile();
|
|
59
|
+
this.saveTrace();
|
|
60
|
+
}, 10000); // Save every 10 seconds
|
|
61
|
+
|
|
62
|
+
// Register exit handlers
|
|
63
|
+
if (!this.exitHandlersRegistered) {
|
|
64
|
+
this.registerExitHandlers();
|
|
65
|
+
this.exitHandlersRegistered = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private async enableCpuProfiling() {
|
|
70
|
+
if (!this.session) return;
|
|
71
|
+
|
|
72
|
+
// Enable profiler
|
|
73
|
+
await new Promise<void>((resolve, reject) => {
|
|
74
|
+
this.session!.post('Profiler.enable', (err) => {
|
|
75
|
+
if (err) reject(err);
|
|
76
|
+
else resolve();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Start sampling
|
|
81
|
+
await new Promise<void>((resolve, reject) => {
|
|
82
|
+
this.session!.post('Profiler.start', {
|
|
83
|
+
// Sample at high frequency for detailed profiling
|
|
84
|
+
samplingInterval: 100
|
|
85
|
+
}, (err) => {
|
|
86
|
+
if (err) reject(err);
|
|
87
|
+
else resolve();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Subscribe to console profile events
|
|
92
|
+
this.session.on('Profiler.consoleProfileStarted', (params: any) => {
|
|
93
|
+
this.traceEvents.push({
|
|
94
|
+
cat: 'disabled-by-default-devtools.timeline',
|
|
95
|
+
name: 'Profile',
|
|
96
|
+
ph: 'P',
|
|
97
|
+
id: params.id || '1',
|
|
98
|
+
pid: this.pid,
|
|
99
|
+
tid: this.tid,
|
|
100
|
+
ts: Date.now() * 1000
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private addMetadataEvents() {
|
|
106
|
+
// Thread name metadata
|
|
107
|
+
this.traceEvents.push({
|
|
108
|
+
args: { name: 'CrRendererMain' },
|
|
109
|
+
cat: '__metadata',
|
|
110
|
+
name: 'thread_name',
|
|
111
|
+
ph: 'M',
|
|
112
|
+
pid: this.pid,
|
|
113
|
+
tid: this.tid,
|
|
114
|
+
ts: 0
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Process name metadata
|
|
118
|
+
this.traceEvents.push({
|
|
119
|
+
args: { name: 'Node.js' },
|
|
120
|
+
cat: '__metadata',
|
|
121
|
+
name: 'process_name',
|
|
122
|
+
ph: 'M',
|
|
123
|
+
pid: this.pid,
|
|
124
|
+
tid: 0,
|
|
125
|
+
ts: 0
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// TracingStartedInBrowser event - required for Chrome DevTools
|
|
129
|
+
this.traceEvents.push({
|
|
130
|
+
args: {
|
|
131
|
+
data: {
|
|
132
|
+
frameTreeNodeId: 1,
|
|
133
|
+
frames: [{
|
|
134
|
+
frame: '0x1',
|
|
135
|
+
name: '',
|
|
136
|
+
processId: this.pid,
|
|
137
|
+
url: 'node://process'
|
|
138
|
+
}],
|
|
139
|
+
persistentIds: true
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
cat: 'disabled-by-default-devtools.timeline',
|
|
143
|
+
name: 'TracingStartedInBrowser',
|
|
144
|
+
ph: 'I',
|
|
145
|
+
pid: this.pid,
|
|
146
|
+
s: 't',
|
|
147
|
+
tid: this.tid,
|
|
148
|
+
ts: this.startTime,
|
|
149
|
+
tts: this.ttsCounter++
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private startMemoryTracking() {
|
|
154
|
+
// Collect memory data every 50ms (matching Chrome's frequency)
|
|
155
|
+
this.memoryInterval = setInterval(() => {
|
|
156
|
+
const memStats = v8.getHeapStatistics();
|
|
157
|
+
const timestamp = Date.now() * 1000;
|
|
158
|
+
|
|
159
|
+
// UpdateCounters event with correct format
|
|
160
|
+
this.traceEvents.push({
|
|
161
|
+
args: {
|
|
162
|
+
data: {
|
|
163
|
+
jsHeapSizeUsed: memStats.used_heap_size,
|
|
164
|
+
jsEventListeners: 0,
|
|
165
|
+
documents: 1,
|
|
166
|
+
nodes: 0
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
cat: 'disabled-by-default-devtools.timeline',
|
|
170
|
+
name: 'UpdateCounters',
|
|
171
|
+
ph: 'I', // Instant event, not Counter
|
|
172
|
+
pid: this.pid,
|
|
173
|
+
s: 't', // Required for instant events
|
|
174
|
+
tid: this.tid,
|
|
175
|
+
ts: timestamp,
|
|
176
|
+
tts: this.ttsCounter++
|
|
177
|
+
});
|
|
178
|
+
}, 50); // Every 50ms
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private saveTrace() {
|
|
182
|
+
const trace = {
|
|
183
|
+
metadata: {
|
|
184
|
+
'command-line': process.argv.join(' '),
|
|
185
|
+
'cpu-brand': 'Node.js V8',
|
|
186
|
+
'dataOrigin': 'TraceEvents',
|
|
187
|
+
'highres-ticks': true,
|
|
188
|
+
'hostname': 'localhost',
|
|
189
|
+
'num-cpus': require('os').cpus().length,
|
|
190
|
+
'physical-memory': require('os').totalmem(),
|
|
191
|
+
'platform': process.platform,
|
|
192
|
+
'process-uptime': process.uptime(),
|
|
193
|
+
'product-version': `Node.js ${process.version}`,
|
|
194
|
+
'protocol-version': '1.0',
|
|
195
|
+
'source': 'NodeProfiler',
|
|
196
|
+
'startTime': new Date(this.startTime / 1000).toISOString(),
|
|
197
|
+
'trace-config': '',
|
|
198
|
+
'user-agent': `Node.js/${process.version}`,
|
|
199
|
+
'v8-version': process.versions.v8
|
|
200
|
+
},
|
|
201
|
+
traceEvents: this.traceEvents
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
fs.writeFileSync(this.tracePath, JSON.stringify(trace, null, 2));
|
|
206
|
+
} catch (e) {
|
|
207
|
+
// Ignore write errors
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private async collectCpuProfile() {
|
|
212
|
+
if (!this.session) return;
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
// Stop current profiling to get samples
|
|
216
|
+
const profile = await new Promise<any>((resolve, reject) => {
|
|
217
|
+
this.session!.post('Profiler.stop', (err, params) => {
|
|
218
|
+
if (err) reject(err);
|
|
219
|
+
else resolve(params.profile);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Convert CPU profile samples to trace events
|
|
224
|
+
if (profile && profile.samples) {
|
|
225
|
+
this.addCpuProfileSamples(profile);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Restart profiling for the next interval
|
|
229
|
+
await new Promise<void>((resolve, reject) => {
|
|
230
|
+
this.session!.post('Profiler.start', {
|
|
231
|
+
samplingInterval: 100
|
|
232
|
+
}, (err) => {
|
|
233
|
+
if (err) reject(err);
|
|
234
|
+
else resolve();
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
} catch (e) {
|
|
238
|
+
// Ignore errors and try to restart
|
|
239
|
+
try {
|
|
240
|
+
await new Promise<void>((resolve, reject) => {
|
|
241
|
+
this.session!.post('Profiler.start', {
|
|
242
|
+
samplingInterval: 100
|
|
243
|
+
}, (err) => {
|
|
244
|
+
if (err) reject(err);
|
|
245
|
+
else resolve();
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
} catch (e2) {
|
|
249
|
+
// Ignore restart errors
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async stop() {
|
|
255
|
+
// Clear intervals
|
|
256
|
+
if (this.memoryInterval) {
|
|
257
|
+
clearInterval(this.memoryInterval);
|
|
258
|
+
this.memoryInterval = undefined;
|
|
259
|
+
}
|
|
260
|
+
if (this.saveInterval) {
|
|
261
|
+
clearInterval(this.saveInterval);
|
|
262
|
+
this.saveInterval = undefined;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Collect final CPU profile
|
|
266
|
+
await this.collectCpuProfile();
|
|
267
|
+
|
|
268
|
+
// Disconnect session
|
|
269
|
+
if (this.session) {
|
|
270
|
+
this.session.disconnect();
|
|
271
|
+
this.session = undefined;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Save final trace
|
|
275
|
+
this.saveTrace();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
private addCpuProfileSamples(profile: any) {
|
|
279
|
+
if (!profile.samples || !profile.timeDeltas || !profile.nodes) return;
|
|
280
|
+
|
|
281
|
+
// Use the last profile time as starting point to maintain continuity
|
|
282
|
+
let currentTime = this.lastProfileTime;
|
|
283
|
+
|
|
284
|
+
// Update nodes map with new nodes
|
|
285
|
+
profile.nodes.forEach((node: any) => {
|
|
286
|
+
this.profileNodes.set(node.id, node);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// Convert samples to trace events with actual function names
|
|
290
|
+
profile.samples.forEach((nodeId: number, index: number) => {
|
|
291
|
+
const node = this.profileNodes.get(nodeId);
|
|
292
|
+
if (!node) return;
|
|
293
|
+
|
|
294
|
+
currentTime += (profile.timeDeltas[index] || 0);
|
|
295
|
+
|
|
296
|
+
const callFrame = node.callFrame;
|
|
297
|
+
if (callFrame) {
|
|
298
|
+
// Clean up function name for display
|
|
299
|
+
let functionName = callFrame.functionName || '(anonymous)';
|
|
300
|
+
if (functionName === '' || functionName === '(root)') {
|
|
301
|
+
functionName = '(program)';
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Extract filename from URL or use a meaningful default
|
|
305
|
+
let url = callFrame.url || '';
|
|
306
|
+
let fileName: string;
|
|
307
|
+
|
|
308
|
+
if (url) {
|
|
309
|
+
// Clean up the URL for display
|
|
310
|
+
if (url.startsWith('file://')) {
|
|
311
|
+
url = url.substring(7);
|
|
312
|
+
}
|
|
313
|
+
const parts = url.split('/');
|
|
314
|
+
fileName = parts[parts.length - 1] || url;
|
|
315
|
+
|
|
316
|
+
// Special handling for node internals
|
|
317
|
+
if (url.startsWith('node:')) {
|
|
318
|
+
fileName = url;
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
// No URL - try to provide context from function name
|
|
322
|
+
if (functionName === '(garbage collector)') {
|
|
323
|
+
fileName = 'v8::gc';
|
|
324
|
+
} else if (functionName === '(idle)') {
|
|
325
|
+
fileName = 'v8::idle';
|
|
326
|
+
} else if (functionName === '(program)') {
|
|
327
|
+
fileName = 'main';
|
|
328
|
+
} else if (functionName.includes('::')) {
|
|
329
|
+
// C++ internal function
|
|
330
|
+
fileName = 'native';
|
|
331
|
+
} else {
|
|
332
|
+
// JavaScript code without source mapping
|
|
333
|
+
fileName = 'javascript';
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
this.traceEvents.push({
|
|
338
|
+
args: {
|
|
339
|
+
data: {
|
|
340
|
+
columnNumber: callFrame.columnNumber || 0,
|
|
341
|
+
frame: `0x${nodeId.toString(16)}`,
|
|
342
|
+
functionName: functionName,
|
|
343
|
+
lineNumber: callFrame.lineNumber || 0,
|
|
344
|
+
scriptId: String(callFrame.scriptId || 0),
|
|
345
|
+
url: url || fileName // Use fileName as URL if no real URL
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
cat: 'devtools.timeline',
|
|
349
|
+
dur: Math.max(profile.timeDeltas[index] || 1, 1),
|
|
350
|
+
name: 'FunctionCall',
|
|
351
|
+
ph: 'X',
|
|
352
|
+
pid: this.pid,
|
|
353
|
+
tid: this.tid,
|
|
354
|
+
ts: Math.floor(currentTime),
|
|
355
|
+
tts: this.ttsCounter++
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// Update lastProfileTime to maintain continuity for the next batch
|
|
361
|
+
this.lastProfileTime = currentTime;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
private registerExitHandlers() {
|
|
365
|
+
const cleanup = async () => {
|
|
366
|
+
await this.stop();
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
process.once('beforeExit', cleanup);
|
|
370
|
+
process.once('SIGINT', cleanup);
|
|
371
|
+
process.once('SIGTERM', cleanup);
|
|
372
|
+
}
|
|
373
|
+
}
|
package/src/rpc/queue.ts
CHANGED
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import {emptyMarkers,
|
|
16
|
+
import {emptyMarkers, Markers} from "../markers";
|
|
17
17
|
import {saveTrace, trace} from "./trace";
|
|
18
18
|
import {createDraft, finishDraft} from "immer";
|
|
19
|
-
import {
|
|
19
|
+
import {isRef, ReferenceMap} from "../reference";
|
|
20
20
|
import {Writable} from "node:stream";
|
|
21
21
|
|
|
22
22
|
/**
|
|
@@ -106,16 +106,6 @@ export class RpcSendQueue {
|
|
|
106
106
|
this.q.push(d);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
sendMarkers<T extends { markers: Markers }>(parent: T, markersFn: (parent: T) => any): Promise<void> {
|
|
110
|
-
return this.getAndSend(parent, t2 => asRef(markersFn(t2)), async (markersRef: Markers & Reference) => {
|
|
111
|
-
await this.getAndSend(markersRef, m => m.id);
|
|
112
|
-
await this.getAndSendList(markersRef,
|
|
113
|
-
(m) => m.markers,
|
|
114
|
-
(marker: Marker) => marker.id
|
|
115
|
-
);
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
109
|
getAndSend<T, U>(parent: T,
|
|
120
110
|
value: (parent: T) => U | undefined,
|
|
121
111
|
onChange?: (value: U) => Promise<any>): Promise<void> {
|
|
@@ -298,6 +288,12 @@ export class RpcReceiveQueue {
|
|
|
298
288
|
before = message.valueType === undefined ?
|
|
299
289
|
message.value :
|
|
300
290
|
this.newObj(message.valueType);
|
|
291
|
+
if (ref !== undefined) {
|
|
292
|
+
// For an object like JavaType that we will mutate in place rather than using
|
|
293
|
+
// immutable updates because of its cyclic nature, the before instance will ultimately
|
|
294
|
+
// be the same as the after instance below.
|
|
295
|
+
this.refs.set(ref, before);
|
|
296
|
+
}
|
|
301
297
|
}
|
|
302
298
|
// Intentional fall-through...
|
|
303
299
|
case RpcObjectState.CHANGE:
|
package/src/rpc/request/parse.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import * as rpc from "vscode-jsonrpc/node";
|
|
17
17
|
import {ExecutionContext} from "../../execution";
|
|
18
18
|
import {UUID} from "node:crypto";
|
|
19
|
-
import {ParserInput, Parsers} from "../../parser";
|
|
19
|
+
import {Parser, ParserInput, Parsers} from "../../parser";
|
|
20
20
|
import {randomId} from "../../uuid";
|
|
21
21
|
import {produce} from "immer";
|
|
22
22
|
import {SourceFile} from "../../tree";
|
package/src/rpc/rewrite-rpc.ts
CHANGED
|
@@ -115,7 +115,7 @@ export class RewriteRpc {
|
|
|
115
115
|
);
|
|
116
116
|
}, this.options.traceGetObjectInput);
|
|
117
117
|
|
|
118
|
-
const remoteObject = await q.receive<P>(
|
|
118
|
+
const remoteObject = await q.receive<P>(localObject);
|
|
119
119
|
|
|
120
120
|
const eof = (await q.take());
|
|
121
121
|
if (eof.state !== RpcObjectState.END_OF_OBJECT) {
|
|
@@ -152,7 +152,8 @@ export class RewriteRpc {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
async print(tree: SourceFile): Promise<string>;
|
|
155
|
-
async print
|
|
155
|
+
async print(tree: Tree, cursor: Cursor): Promise<string>;
|
|
156
|
+
async print(tree: Tree, cursor?: Cursor): Promise<string> {
|
|
156
157
|
if (!cursor && !isSourceFile(tree)) {
|
|
157
158
|
throw new Error("Cursor is required for non-SourceFile trees");
|
|
158
159
|
}
|
package/src/rpc/server.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {RewriteRpc} from "./rewrite-rpc";
|
|
|
20
20
|
import * as fs from "fs";
|
|
21
21
|
import {Command} from 'commander';
|
|
22
22
|
import {dir} from 'tmp-promise';
|
|
23
|
+
import {ChromeProfiler} from './chrome-profiler';
|
|
23
24
|
|
|
24
25
|
// Include all languages you want this server to support.
|
|
25
26
|
import "../text";
|
|
@@ -28,7 +29,7 @@ import "../java";
|
|
|
28
29
|
import "../javascript";
|
|
29
30
|
|
|
30
31
|
// Not possible to set the stack size when executing from npx for security reasons
|
|
31
|
-
require('v8').setFlagsFromString('--stack-size=
|
|
32
|
+
require('v8').setFlagsFromString('--stack-size=8000');
|
|
32
33
|
|
|
33
34
|
interface ProgramOptions {
|
|
34
35
|
logFile?: string;
|
|
@@ -37,6 +38,7 @@ interface ProgramOptions {
|
|
|
37
38
|
traceGetObjectOutput?: boolean;
|
|
38
39
|
traceGetObjectInput?: boolean;
|
|
39
40
|
recipeInstallDir?: string;
|
|
41
|
+
profile?: boolean;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
async function main() {
|
|
@@ -49,10 +51,18 @@ async function main() {
|
|
|
49
51
|
.option('--trace-get-object-output', 'enable `GetObject` output tracing')
|
|
50
52
|
.option('--trace-get-object-input', 'enable `GetObject` input tracing')
|
|
51
53
|
.option('--recipe-install-dir <install_dir>', 'Recipe installation directory (default is a temporary directory)')
|
|
54
|
+
.option('--profile', 'enable profiling')
|
|
52
55
|
.parse();
|
|
53
56
|
|
|
54
57
|
const options = program.opts() as ProgramOptions;
|
|
55
58
|
|
|
59
|
+
// Chrome profiling
|
|
60
|
+
let profiler: ChromeProfiler | undefined;
|
|
61
|
+
if (options.profile) {
|
|
62
|
+
profiler = new ChromeProfiler();
|
|
63
|
+
profiler.start().catch(console.error);
|
|
64
|
+
}
|
|
65
|
+
|
|
56
66
|
let recipeInstallDir: string;
|
|
57
67
|
if (!options.recipeInstallDir) {
|
|
58
68
|
let recipeCleanup: () => Promise<void>;
|
|
@@ -65,6 +75,9 @@ async function main() {
|
|
|
65
75
|
|
|
66
76
|
// Register cleanup on exit
|
|
67
77
|
process.on('SIGINT', async () => {
|
|
78
|
+
if (profiler) {
|
|
79
|
+
await profiler.stop();
|
|
80
|
+
}
|
|
68
81
|
if (recipeCleanup) {
|
|
69
82
|
await recipeCleanup();
|
|
70
83
|
}
|
|
@@ -72,6 +85,9 @@ async function main() {
|
|
|
72
85
|
});
|
|
73
86
|
|
|
74
87
|
process.on('SIGTERM', async () => {
|
|
88
|
+
if (profiler) {
|
|
89
|
+
await profiler.stop();
|
|
90
|
+
}
|
|
75
91
|
if (recipeCleanup) {
|
|
76
92
|
await recipeCleanup();
|
|
77
93
|
}
|
|
@@ -81,6 +97,19 @@ async function main() {
|
|
|
81
97
|
recipeInstallDir = await setupRecipeDir();
|
|
82
98
|
} else {
|
|
83
99
|
recipeInstallDir = options.recipeInstallDir;
|
|
100
|
+
|
|
101
|
+
// Register cleanup for profiler when no recipe cleanup is needed
|
|
102
|
+
if (profiler) {
|
|
103
|
+
process.on('SIGINT', async () => {
|
|
104
|
+
await profiler.stop();
|
|
105
|
+
process.exit(0);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
process.on('SIGTERM', async () => {
|
|
109
|
+
await profiler.stop();
|
|
110
|
+
process.exit(0);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
84
113
|
}
|
|
85
114
|
|
|
86
115
|
const log = options.logFile ? fs.createWriteStream(options.logFile, {flags: 'a'}) : undefined;
|
package/src/test/rewrite-test.ts
CHANGED
|
@@ -61,8 +61,20 @@ export class RecipeSpec {
|
|
|
61
61
|
this.dataTableAssertions[name] = allRows;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
async rewriteRun(...sourceSpecs: SourceSpec<any>[]): Promise<void> {
|
|
65
|
-
|
|
64
|
+
async rewriteRun(...sourceSpecs: (SourceSpec<any> | Generator<SourceSpec<any>, void, unknown>)[]): Promise<void> {
|
|
65
|
+
// Flatten generators into a list of sourceSpecs
|
|
66
|
+
const flattenedSpecs: SourceSpec<any>[] = [];
|
|
67
|
+
for (const specOrGenerator of sourceSpecs) {
|
|
68
|
+
if (specOrGenerator && typeof (specOrGenerator as any).next === 'function') {
|
|
69
|
+
for (const spec of specOrGenerator as Generator<SourceSpec<any>, void, unknown>) {
|
|
70
|
+
flattenedSpecs.push(spec);
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
flattenedSpecs.push(specOrGenerator as SourceSpec<any>);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const specsByKind = flattenedSpecs.reduce((groups, spec) => {
|
|
66
78
|
const kind = spec.kind;
|
|
67
79
|
if (!groups[kind]) {
|
|
68
80
|
groups[kind] = [];
|
|
@@ -179,7 +191,11 @@ export class RecipeSpec {
|
|
|
179
191
|
const b = spec.beforeRecipe ? spec.beforeRecipe(sourceFile) : sourceFile;
|
|
180
192
|
if (b !== undefined) {
|
|
181
193
|
if (b instanceof Promise) {
|
|
182
|
-
|
|
194
|
+
const mapped = await b;
|
|
195
|
+
if (mapped === undefined) {
|
|
196
|
+
throw new Error("Expected beforeRecipe to return a SourceFile, but got undefined. Did you forget a return statement?");
|
|
197
|
+
}
|
|
198
|
+
return [spec, mapped];
|
|
183
199
|
}
|
|
184
200
|
return [spec, b as SourceFile];
|
|
185
201
|
}
|
package/src/text/tree.ts
CHANGED
package/src/tree.ts
CHANGED
|
@@ -77,6 +77,21 @@ export class Cursor {
|
|
|
77
77
|
return path;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
parentTree(level: number = 1): Cursor | undefined {
|
|
81
|
+
let c: Cursor | undefined = this.parent;
|
|
82
|
+
let treeCount = 0;
|
|
83
|
+
while (c) {
|
|
84
|
+
if (isTree(c.value)) {
|
|
85
|
+
treeCount++;
|
|
86
|
+
if (treeCount === level) {
|
|
87
|
+
return c;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
c = c.parent;
|
|
91
|
+
}
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
80
95
|
firstEnclosing<T>(match: (value: any) => value is T): T | undefined {
|
|
81
96
|
let c: Cursor | undefined = this;
|
|
82
97
|
while (c) {
|