@@ -176,6 +176,12 @@ export function transform(
176176
177177 if ( transformedFunctionInfos . length === 0 ) return program ;
178178
179+ // Add these to prevent crash from shadowing of "global" and/or "globalThis".
180+ if ( config ( ) . generateGlobalRecordHookCheck ) {
181+ program . body . unshift ( __appmapRecordVariableDeclaration ) ;
182+ program . body . unshift ( __appmapRecordInitFunctionDeclaration ) ;
183+ }
184+
179185 // Add a global variable to hold the function registry (see recorder.ts)
180186 const functionRegistryAssignment : ESTree . VariableDeclaration = {
181187 type : "VariableDeclaration" ,
@@ -253,12 +259,12 @@ function wrapCallable(
253259) : ESTree . CallExpression {
254260 // (1) Pass the function as an arrow function expression even if the original
255261 // function is not an arrow function, because of a potential super keyword inside.
256- // global.AppMapRecordHook .call(this|undefined, () => {...}, arguments, __appmapFunctionRegistry[i])
262+ // __appmapRecord .call(this|undefined, () => {...}, arguments, __appmapFunctionRegistry[i])
257263 //
258264 // (2) If it's a generator function then pass it as a generator function since
259265 // yield keyword cannot be used inside an arrow function. We don't care about (1)
260266 // since we don't transform generator methods due to the potential super keyword inside.
261- // yield* global.AppMapRecordHook .call(this|undefined, function* f() {...}, arguments, __appmapFunctionRegistry[i])
267+ // yield* __appmapRecord .call(this|undefined, function* f() {...}, arguments, __appmapFunctionRegistry[i])
262268
263269 let functionArgument : ESTree . Expression =
264270 fd . type === "ArrowFunctionExpression"
@@ -279,7 +285,9 @@ function wrapCallable(
279285 if ( fd . type != "ArrowFunctionExpression" ) functionArgument = { ...functionArgument , params : [ ] } ;
280286
281287 return call_ (
282- memberId ( "global" , "AppMapRecordHook" , "call" ) ,
288+ config ( ) . generateGlobalRecordHookCheck
289+ ? memberId ( "__appmapRecord" , "call" )
290+ : memberId ( "global" , "AppMapRecordHook" , "call" ) ,
283291 thisArg ,
284292 functionArgument ,
285293 argsArg ,
@@ -352,3 +360,27 @@ function exportName(expr: ESTree.Expression): ESTree.Identifier | undefined {
352360 }
353361 return undefined ;
354362}
363+
364+ export const __appmapRecordVariableDeclarationCode = `const __appmapRecord = __appmapRecordInit()` ;
365+ const __appmapRecordVariableDeclaration = parse ( __appmapRecordVariableDeclarationCode , {
366+ module : true ,
367+ } ) . body [ 0 ] as ESTree . VariableDeclaration ;
368+
369+ export const __appmapRecordInitFunctionDeclarationCode = `
370+ function __appmapRecordInit() {
371+ let g = null;
372+ try {
373+ g = global.AppMapRecordHook;
374+ } catch (e) {
375+ try {
376+ g = globalThis.AppMapRecordHook;
377+ } catch (e) {}
378+ // If global/globalThis is shadowed in the top level, we'll get:
379+ // ReferenceError: Cannot access 'global' before initialization
380+ }
381+ // Bypass recording if we can't access recorder to prevent a crash.
382+ return g ?? ((fun, argsArg) => fun.apply(this, argsArg));
383+ }` ;
384+ const __appmapRecordInitFunctionDeclaration = parse ( __appmapRecordInitFunctionDeclarationCode , {
385+ module : true ,
386+ } ) . body [ 0 ] as ESTree . FunctionDeclaration ;
0 commit comments