1
1
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 ,
2
12
DataExtractorApi ,
13
+ DataExtractorContext ,
3
14
DataResult ,
4
15
JSONString ,
5
- DataExtractor ,
6
- DataExtraction ,
7
- ExtractionCollector ,
8
- DataExtractorContext ,
16
+ SkippedCallFrames ,
9
17
} from "./DataExtractorApi" ;
10
- import {
11
- DataExtractorInfo ,
12
- VisualizationData ,
13
- } from "../../DataExtractionResult" ;
14
- import { registerDefaultExtractors } from "./default-extractors" ;
15
18
import { LoadDataExtractorsFn } from "./LoadDataExtractorsFn" ;
16
- import * as helpers from "../helpers " ;
19
+ import { registerDefaultExtractors } from "./default-extractors " ;
17
20
18
21
/**
19
22
* @internal
@@ -42,58 +45,39 @@ export class DataExtractorApiImpl implements DataExtractorApi {
42
45
valueFn : ( ) => unknown ,
43
46
evalFn : < T > ( expression : string ) => T ,
44
47
preferredDataExtractorId : string | undefined ,
45
- variablesInScope : Record < string , ( ) => unknown >
48
+ variablesInScope : Record < string , ( ) => unknown > ,
49
+ callFramesSnapshot : CallFramesSnapshot | null
46
50
) : 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 [ ] = [ ] ;
84
52
const rootContext = new ContextImpl (
85
53
variablesInScope ,
86
54
removeEnd ( removeStart ( valueFn . toString ( ) , "() => (" ) , ")" ) . trim ( ) ,
87
55
evalFn ,
88
56
this ,
89
- undefined
57
+ undefined ,
58
+ callFramesSnapshot ?. frames ?? [ ] ,
59
+ callFrameRequests
90
60
) ;
91
61
92
62
DataExtractorApiImpl . lastContext = rootContext ;
93
63
const value = valueFn ( ) ;
94
64
const extractions = this . getExtractions ( value , rootContext ) ;
95
65
DataExtractorApiImpl . lastContext = undefined ;
96
66
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
+
97
81
let usedExtraction = extractions [ 0 ] ;
98
82
if ( ! usedExtraction ) {
99
83
return this . toJson ( { kind : "NoExtractors" } as DataResult ) ;
@@ -108,14 +92,6 @@ export class DataExtractorApiImpl implements DataExtractorApi {
108
92
}
109
93
}
110
94
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
-
119
95
const data = usedExtraction . extractData ( ) ;
120
96
return this . toJson ( {
121
97
kind : "Data" ,
@@ -188,6 +164,60 @@ export class DataExtractorApiImpl implements DataExtractorApi {
188
164
}
189
165
}
190
166
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
+
191
221
function removeStart ( str : string , start : string ) : string {
192
222
if ( str . startsWith ( start ) ) {
193
223
return str . substr ( start . length ) ;
@@ -201,3 +231,20 @@ function removeEnd(str: string, end: string): string {
201
231
}
202
232
return str ;
203
233
}
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