Skip to content

Commit a9656e1

Browse files
committed
Update
1 parent d45dfda commit a9656e1

37 files changed

+5530
-3692
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,5 @@ out/
8686
__pycache__/
8787
*.py[cod]
8888
*$py.class
89+
90+
*.vsix

.prettierrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
"trailingComma": "es5",
33
"tabWidth": 4,
44
"semi": true,
5-
"useTabs": true
5+
"useTabs": true,
6+
"printWidth": 120
67
}

.vscode/settings.json

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@
77
"plantuml.exportOutDir": "docs/exported",
88
"typescript.updateImportsOnFileMove.enabled": "prompt",
99
"mochaExplorer.files": "./data-extraction/test/**/*.test.ts",
10-
"mochaExplorer.require": [
11-
"ts-node/register",
12-
"source-map-support/register"
13-
],
10+
"mochaExplorer.require": ["ts-node/register", "source-map-support/register"],
1411
"tasksStatusbar.taskLabelFilter": "dev",
1512
"editor.formatOnSave": true,
16-
"typescript.tsdk": "node_modules\\typescript\\lib"
13+
"typescript.tsdk": "node_modules\\typescript\\lib",
14+
15+
"editor.defaultFormatter": "esbenp.prettier-vscode",
16+
"prettier.prettierPath": "node_modules/prettier",
17+
"prettier.printWidth": 120,
18+
"[javascript]": {
19+
"editor.defaultFormatter": "esbenp.prettier-vscode"
20+
}
1721
}

data-extraction/package.json

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
},
2929
"dependencies": {},
3030
"devDependencies": {
31+
"copy-webpack-plugin": "^11.0.0",
32+
"@types/copy-webpack-plugin": "^10.1.0",
3133
"@types/mocha": "^7.0.2",
3234
"@types/node": "^13.7.4",
3335
"@types/plotly.js": "1.44.28",
@@ -36,10 +38,11 @@
3638
"mocha-lcov-reporter": "^1.3.0",
3739
"nyc": "^15.0.0",
3840
"source-map-support": "^0.5.16",
39-
"ts-loader": "^6.2.1",
40-
"tslint": "^6.0.0",
41-
"typescript": "^3.8.2",
42-
"webpack": "^4.41.6",
43-
"webpack-cli": "^3.3.11"
41+
"tslint": "^6.1.3",
42+
"typescript": "^5.1.6",
43+
"webpack": "^5.88.1",
44+
"webpack-cli": "^5.1.4",
45+
"ts-loader": "^9.4.4",
46+
"ts-node": "^10.9.1"
4447
}
4548
}

data-extraction/src/getGlobal.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,5 @@ export function getGlobal(): any {
66
} else if (typeof window === "object") {
77
return window;
88
}
9-
109
throw new Error("No global available");
1110
}

data-extraction/src/js/api/DataExtractorApi.ts

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
import {
2-
VisualizationData,
3-
DataExtractionResult,
4-
} from "../../DataExtractionResult";
1+
import { DataExtractionResult, VisualizationData } from "../../DataExtractionResult";
52
import { LoadDataExtractorsFn } from "./LoadDataExtractorsFn";
63

74
export interface DataExtractorApi {
@@ -24,7 +21,8 @@ export interface DataExtractorApi {
2421
valueFn: () => unknown,
2522
evalFn: <T>(expression: string) => T,
2623
preferredDataExtractorId: string | undefined,
27-
variablesInScope: Record<string, unknown>
24+
variablesInScope: Record<string, unknown>,
25+
callFramesSnapshot: CallFramesSnapshot | null
2826
): JSONString<DataResult>;
2927

3028
/**
@@ -42,12 +40,41 @@ export type DataResult =
4240
extractionResult: DataExtractionResult;
4341
}
4442
| { kind: "NoExtractors" }
45-
| { kind: "Error"; message: string };
43+
| { kind: "Error"; message: string }
44+
| {
45+
kind: "OutdatedCallFrameSnapshot";
46+
callFramesRequest: CallFramesRequest;
47+
};
4648

4749
export interface JSONString<T> extends String {
4850
__brand: { json: T };
4951
}
5052

53+
export interface CallFramesRequest {
54+
requestId: string;
55+
requestedCallFrames: CallFrameRequest[];
56+
}
57+
58+
export interface CallFrameRequest {
59+
methodName: string;
60+
pathRegExp: string | undefined;
61+
}
62+
63+
export interface CallFramesSnapshot {
64+
requestId: string;
65+
frames: (CallFrameInfo | SkippedCallFrames)[];
66+
}
67+
68+
export interface SkippedCallFrames {
69+
skippedFrames: number;
70+
}
71+
72+
export interface CallFrameInfo {
73+
methodName: string;
74+
source: { name: string; path: string };
75+
vars: Record<string, any>;
76+
}
77+
5178
export interface DataExtractor {
5279
/**
5380
* Must be unique among all data extractors.
@@ -58,11 +85,7 @@ export interface DataExtractor {
5885
* Filters the data to be extracted.
5986
*/
6087
dataCtor?: string;
61-
getExtractions(
62-
data: unknown,
63-
extractionCollector: ExtractionCollector,
64-
context: DataExtractorContext
65-
): void;
88+
getExtractions(data: unknown, extractionCollector: ExtractionCollector, context: DataExtractorContext): void;
6689
}
6790

6891
export interface ExtractionCollector {
@@ -78,11 +101,15 @@ export interface DataExtractorContext {
78101
*/
79102
evalFn: <TEval>(expression: string) => TEval;
80103

81-
expression: string | undefined;
104+
readonly expression: string | undefined;
82105

83-
variablesInScope: Record<string, () => unknown>;
106+
readonly variablesInScope: Record<string, () => unknown>;
84107

85108
extract(value: unknown): VisualizationData | undefined;
109+
110+
readonly callFrameInfos: readonly (CallFrameInfo | SkippedCallFrames)[];
111+
112+
addCallFrameRequest(request: CallFrameRequest): void;
86113
}
87114

88115
export interface DataExtraction {

data-extraction/src/js/api/DataExtractorApiImpl.ts

Lines changed: 104 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
import {
2+
DataExtractorInfo,
3+
VisualizationData,
4+
} from "../../DataExtractionResult";
5+
import * as helpers from "../helpers";
6+
import {
7+
CallFrameInfo,
8+
CallFrameRequest,
9+
CallFramesSnapshot,
10+
DataExtraction,
11+
DataExtractor,
212
DataExtractorApi,
13+
DataExtractorContext,
314
DataResult,
415
JSONString,
5-
DataExtractor,
6-
DataExtraction,
7-
ExtractionCollector,
8-
DataExtractorContext,
16+
SkippedCallFrames,
917
} from "./DataExtractorApi";
10-
import {
11-
DataExtractorInfo,
12-
VisualizationData,
13-
} from "../../DataExtractionResult";
14-
import { registerDefaultExtractors } from "./default-extractors";
1518
import { LoadDataExtractorsFn } from "./LoadDataExtractorsFn";
16-
import * as helpers from "../helpers";
19+
import { registerDefaultExtractors } from "./default-extractors";
1720

1821
/**
1922
* @internal
@@ -42,58 +45,39 @@ export class DataExtractorApiImpl implements DataExtractorApi {
4245
valueFn: () => unknown,
4346
evalFn: <T>(expression: string) => T,
4447
preferredDataExtractorId: string | undefined,
45-
variablesInScope: Record<string, () => unknown>
48+
variablesInScope: Record<string, () => unknown>,
49+
callFramesSnapshot: CallFramesSnapshot | null
4650
): JSONString<DataResult> {
47-
class ContextImpl implements DataExtractorContext {
48-
constructor(
49-
public readonly variablesInScope: Record<string, () => unknown>,
50-
public readonly expression: string | undefined,
51-
public readonly evalFn: <T>(expression: string) => T,
52-
private readonly _api: DataExtractorApiImpl,
53-
private readonly _parent: ContextImpl | undefined
54-
) {}
55-
56-
get _level(): number {
57-
return this._parent ? this._parent._level + 1 : 0;
58-
}
59-
60-
extract(value: any): VisualizationData | undefined {
61-
if (this._level > 10) {
62-
throw new Error(
63-
"extract() called too many times recursively"
64-
);
65-
}
66-
67-
const extractions = this._api.getExtractions(
68-
value,
69-
new ContextImpl(
70-
this.variablesInScope,
71-
undefined,
72-
this.evalFn,
73-
this._api,
74-
this
75-
)
76-
);
77-
if (extractions.length === 0) {
78-
return undefined;
79-
}
80-
return extractions[0].extractData();
81-
}
82-
}
83-
51+
const callFrameRequests: CallFrameRequest[] = [];
8452
const rootContext = new ContextImpl(
8553
variablesInScope,
8654
removeEnd(removeStart(valueFn.toString(), "() => ("), ")").trim(),
8755
evalFn,
8856
this,
89-
undefined
57+
undefined,
58+
callFramesSnapshot?.frames ?? [],
59+
callFrameRequests
9060
);
9161

9262
DataExtractorApiImpl.lastContext = rootContext;
9363
const value = valueFn();
9464
const extractions = this.getExtractions(value, rootContext);
9565
DataExtractorApiImpl.lastContext = undefined;
9666

67+
const requestId =
68+
callFrameRequests.length === 0
69+
? ""
70+
: "" + cyrb53(JSON.stringify(callFrameRequests));
71+
if ((callFramesSnapshot?.requestId ?? "") !== requestId) {
72+
return this.toJson({
73+
kind: "OutdatedCallFrameSnapshot",
74+
callFramesRequest: {
75+
requestId,
76+
requestedCallFrames: callFrameRequests,
77+
},
78+
});
79+
}
80+
9781
let usedExtraction = extractions[0];
9882
if (!usedExtraction) {
9983
return this.toJson({ kind: "NoExtractors" } as DataResult);
@@ -108,14 +92,6 @@ export class DataExtractorApiImpl implements DataExtractorApi {
10892
}
10993
}
11094

111-
function mapExtractor(e: DataExtraction): DataExtractorInfo {
112-
return {
113-
id: e.id! as any,
114-
name: e.name!,
115-
priority: e.priority,
116-
};
117-
}
118-
11995
const data = usedExtraction.extractData();
12096
return this.toJson({
12197
kind: "Data",
@@ -188,6 +164,60 @@ export class DataExtractorApiImpl implements DataExtractorApi {
188164
}
189165
}
190166

167+
function mapExtractor(e: DataExtraction): DataExtractorInfo {
168+
return {
169+
id: e.id! as any,
170+
name: e.name!,
171+
priority: e.priority,
172+
};
173+
}
174+
175+
class ContextImpl implements DataExtractorContext {
176+
constructor(
177+
public readonly variablesInScope: Record<string, () => unknown>,
178+
public readonly expression: string | undefined,
179+
public readonly evalFn: <T>(expression: string) => T,
180+
private readonly _api: DataExtractorApiImpl,
181+
private readonly _parent: ContextImpl | undefined,
182+
public readonly callFrameInfos: readonly (
183+
| CallFrameInfo
184+
| SkippedCallFrames
185+
)[],
186+
private readonly _callFrameRequests: CallFrameRequest[]
187+
) {}
188+
189+
addCallFrameRequest(request: CallFrameRequest): void {
190+
this._callFrameRequests.push(request);
191+
}
192+
193+
get _level(): number {
194+
return this._parent ? this._parent._level + 1 : 0;
195+
}
196+
197+
extract(value: any): VisualizationData | undefined {
198+
if (this._level > 10) {
199+
throw new Error("extract() called too many times recursively");
200+
}
201+
202+
const extractions = this._api.getExtractions(
203+
value,
204+
new ContextImpl(
205+
this.variablesInScope,
206+
undefined,
207+
this.evalFn,
208+
this._api,
209+
this,
210+
this.callFrameInfos,
211+
this._callFrameRequests
212+
)
213+
);
214+
if (extractions.length === 0) {
215+
return undefined;
216+
}
217+
return extractions[0].extractData();
218+
}
219+
}
220+
191221
function removeStart(str: string, start: string): string {
192222
if (str.startsWith(start)) {
193223
return str.substr(start.length);
@@ -201,3 +231,20 @@ function removeEnd(str: string, end: string): string {
201231
}
202232
return str;
203233
}
234+
235+
// From https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript
236+
function cyrb53(str: string, seed = 0) {
237+
let h1 = 0xdeadbeef ^ seed,
238+
h2 = 0x41c6ce57 ^ seed;
239+
for (let i = 0, ch; i < str.length; i++) {
240+
ch = str.charCodeAt(i);
241+
h1 = Math.imul(h1 ^ ch, 2654435761);
242+
h2 = Math.imul(h2 ^ ch, 1597334677);
243+
}
244+
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
245+
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
246+
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
247+
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
248+
249+
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
250+
}

0 commit comments

Comments
 (0)