mORMot and Open Source friends
Check-in [f9821cf707]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:SyNode: add VSCode debugging compatibility & sourceMap support. Thanks George for patch
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: f9821cf7079266f515df5bc3e0c0d8b3a91fdbfa
User & Date: mpv 2017-09-30 13:25:41
Context
2017-10-02
08:21
{3855} introducing TDDDSocketThreadSettings.SocketBufferBytes check-in: fa7a121d1d user: ab tags: trunk
2017-09-30
13:25
SyNode: add VSCode debugging compatibility & sourceMap support. Thanks George for patch check-in: f9821cf707 user: mpv tags: trunk
09:54
Prevent AV in CrtSocket.Close in case CreateSockOut not called check-in: 6771dd8862 user: mpv tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to SyNode/SyNode.pas.

34
35
36
37
38
39
40

41
42
43
44
45
46
47
..
71
72
73
74
75
76
77


78
79
80
81
82
83
84
...
152
153
154
155
156
157
158

159
160
161
162
163
164
165
...
226
227
228
229
230
231
232
233



234
235
236
237
238
239
240
...
308
309
310
311
312
313
314


315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
...
352
353
354
355
356
357
358
359
360
361
362
363

364
365
366
367
368
369
370
...
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
...
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
...
477
478
479
480
481
482
483




484
485
486
487
488
489
490
....
1294
1295
1296
1297
1298
1299
1300





1301
1302
1303
1304
1305
1306
1307
....
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
....
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
....
1500
1501
1502
1503
1504
1505
1506







1507
1508
1509
1510
1511
1512
1513
  the Initial Developer. All Rights Reserved.

  Contributor(s):
  - Arnaud Bouchez
  - Vadim Orel
  - Pavel Mashlyakovsky
  - win2014


  Alternatively, the contents of this file may be used under the terms of
  either the GNU General Public License Version 2 or later (the "GPL"), or
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  in which case the provisions of the GPL or the LGPL are applicable instead
  of those above. If you wish to allow use of your version of this file only
  under the terms of either the GPL or the LGPL, and not to allow others to
................................................................................
  - move a Worker classes from UnityBase to SyNode
    Allow to create and communicate with a threads from JavaScript
  - implement nodeJS Buffer
  - improved compartibility with nodeJS utils
  - add a `process.version`
  - implement a setTimeout/setInverval/setImmediate etc.
  - SpiderMonkey 52 (x32 & x64) support ( SM52 condition should be defined )



}

{$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64 OWNNORMTOUPPER
{$I SyNode.inc}   // define SM_DEBUG CONSIDER_TIME_IN_Z

interface
................................................................................
    fLastErrorFileName: RawUTF8;
    fLastErrorLine: integer;
    fLastErrorStackTrace: SynUnicode;
    fErrorExist: boolean;
    fThreadData: pointer;
    fDllModulesUnInitProcList: TList;
    fNeverExpire: boolean;

    /// called from SpiderMonkey callback. Do not raise exception here
    // instead use CheckJSError metod after JSAPI compile/evaluate call
{$IFDEF SM52}
    procedure DoProcessJSError(report: PJSErrorReport); virtual;
{$ELSE}
    procedure DoProcessJSError(errMsg: PCChar; report: PJSErrorReport); virtual;
{$ENDIF}
................................................................................
    /// evaluate a JavaScript script in the global scope
    // - if exception raised in script - raise Delphi ESMException
    // - on success returns last executed expression statement processed
    // in the script as a variant
    // - JavaScript equivalent to
    // ! eval(script)
    procedure Evaluate(const script: SynUnicode; const scriptName: RawUTF8;
      lineNo: Cardinal; out result: jsval);




    /// evaluate a JavaScript script as Module
    // - if exception raised in script - raise Delphi ESMException
    // - JavaScript equivalent to import of ES6
    function EvaluateModule(const scriptName: RawUTF8): jsval;

    /// run method in object
................................................................................
    procedure DefineNodeProcess;
    procedure DefineModuleLoader;

    /// Used by debugger
    property PrivateDataForDebugger: Pointer read FPrivateDataForDebugger write SetPrivateDataForDebugger;
    /// Name of this engine. Will be shown in debugger for indentify this engine
    property nameForDebug: RawUTF8 read fnameForDebug;


    /// Handler hor debugger. Called from debugger thread when debugger need
    // call rt.InterruptCallback(cx) in engine''s thread
    // this method must wake up the engine's thread and thread must
    // execute rt.InterruptCallback(cx) for this engine
    property doInteruptInOwnThread: TThreadMethod read fDoInteruptInOwnThread write fDoInteruptInOwnThread;
  end;


  /// prototype of SpideMonkey notification callback method
  TEngineEvent = procedure(const Engine: TSMEngine) of object;

  /// event of getting engine name
  TEngineNameEvent = function(const Engine: TSMEngine): RawUTF8 of object;

  /// event for loading dll module
  TDllModuleEvent = procedure(Handle: HMODULE) of object;

  /// event for adding values to process.binding
  TSMProcessBindingHandler = function(const Engine: TSMEngine; const bindingNamespaceName: SynUnicode): jsval;
................................................................................
  TSMEngineManager = class
  private
    FMaxPerEngineMemory: Cardinal;
    FMaxNurseryBytes: Cardinal;
    FMaxRecursionDepth: Cardinal;
    FEnginePool: TObjectListLocked;
    FRemoteDebuggerThread: TThread;
    FFakeXPIInstallThread: TThread;
    FContentVersion: Cardinal;
    FOnNewEngine: TEngineEvent;
    FOnDebuggerInit: TEngineEvent;
    FOnGetName: TEngineNameEvent;

    FOnDebuggerConnected: TEngineEvent;
    FOnDllModuleLoaded: TDllModuleEvent;
    {$ifdef ISDELPHIXE2}
    FRttiCx: TRttiContext;
    {$endif}
    FEngineClass: TSMEngineClass;

................................................................................
    /// Release a javaScript engine for specified thread
    procedure ReleaseEngineForThread(aThreadID: DWORD);
    /// returns -1 if none was defined yet
    // - this method is not protected via the global FEngineCS mutex/lock
    function ThreadEngineIndex(ThreadID: DWORD): Integer;
    /// returns nil if none was defined yet
    function CurrentThreadEngine: TSMEngine;
    /// create a new SpiderMonkey Engine
    // - used by ThreadSafeEngine method to instantiate a new per-thread Engine
    function CreateNewEngine(pThreadData: pointer): TSMEngine; virtual;
    /// called when a new Engine is created
    // - this default implementation will run the OnNewEngine callback (if any)
    procedure DoOnNewEngine(const Engine: TSMEngine); virtual;

    function getPauseDebuggerOnFirstStep: boolean;
    procedure setPauseDebuggerOnFirstStep(const Value: boolean);

................................................................................
    {$endif}
    /// Start/stop debugger listening on selected port
    // - expects the port to be specified as Ansi string, e.g. '1234'
    // - you can optionally specify a server address to bind to, e.g.
    // '1.2.3.4:1234'
    // - debugger create a dedicated thread where it listen to a requests
    // from a remote debug UI
    procedure startDebugger(const port: SockString = '6000'; withFakeXPIInstaller: boolean = false);
    procedure stopDebugger;
    /// Write text as console log to current thread Engine's debugger (if exists)
    procedure debuggerLog(const Text: RawUTF8);
    /// when debugger connected to new engine this Engine must pause on first step
    property pauseDebuggerOnFirstStep: boolean read getPauseDebuggerOnFirstStep write setPauseDebuggerOnFirstStep;
    /// Workers manager
    property WorkersManager: TJSWorkersManager read FWorkersManager;
................................................................................
      read GetEngineExpireTimeOutMinutes write SetEngineExpireTimeOutMinutes default 0;
    /// Path to core synode modules
    property CoreModulesPath: RawUTF8 read FCoreModulesPath;
    /// event triggered every time a new Engine is created
    // event trigered before OnDebuggerInit and OnNewEngine events
    // Result og this method is Engine's name for debug
    property OnGetName: TEngineNameEvent read FOnGetName write FOnGetName;





    /// event triggered every time a internal debugger process connected to Engine
    // - event trigered in debugger's compartment
    // - here your code can change the initial state of the debugger
    property OnDebuggerInit: TEngineEvent read FOnDebuggerInit write FOnDebuggerInit;

    /// event triggered every time a new Engine is created
................................................................................
      Result.SetThreadData(pThreadData);

    if WorkersManager.curThreadIsWorker then
      Result.fnameForDebug := WorkersManager.getCurrentWorkerThreadName
    else if Assigned(OnGetName) then
      Result.fnameForDebug := OnGetName(Result);






    result.fThreadID := ThreadID;
    FEnginePool.Add(result);
  finally
    FEnginePool.Safe.UnLock;
  end;

  if FRemoteDebuggerThread <> nil then
................................................................................
procedure TSMEngineManager.setPauseDebuggerOnFirstStep(const Value: boolean);
begin
  if FRemoteDebuggerThread<> nil then begin
    TSMRemoteDebuggerThread(FRemoteDebuggerThread).NeedPauseOnFirstStep := Value;
  end
end;

procedure TSMEngineManager.startDebugger(const port: SockString = '6000'; withFakeXPIInstaller: boolean = false);
begin
  FRemoteDebuggerThread := TSMRemoteDebuggerThread.Create(self, port);
  if withFakeXPIInstaller then
    FFakeXPIInstallThread := TFakeXPIInstaller.Create();
  inc(FContentVersion);
end;

procedure TSMEngineManager.stopDebugger;
begin
  if FRemoteDebuggerThread <> nil then begin
    TSMRemoteDebuggerThread(FRemoteDebuggerThread).SetTerminated;
    FRemoteDebuggerThread := nil;
    if FFakeXPIInstallThread <> nil then begin
      TFakeXPIInstaller(FFakeXPIInstallThread).SetTerminated;
      FFakeXPIInstallThread := nil;
    end;
  end;
end;

function StringReplaceChars(const Source: String; OldChar, NewChar: Char): String;
var i,j,n: integer;
begin
  if (OldChar<>NewChar) and (Source<>'') then begin
    n := length(Source);
    for i := 0 to n-1 do
................................................................................

  if ACheckResultInsideBase and
     ((length(Result) < length(ABaseDir)) or (length(ABaseDir)=0) or
      (StrLIComp(PChar(@Result[1]), @localBase[1], length(localBase)) <> 0)) then
    Result := ''
end;

function TSMEngineManager.CreateNewEngine(pThreadData: pointer): TSMEngine;
begin
  Result := FEngineClass.Create(Self);
  if (pThreadData <> nil) then
    Result.SetThreadData(pThreadData);
  if Assigned(OnGetName) then
    Result.fnameForDebug := OnGetName(Result);

  if FRemoteDebuggerThread <> nil then
    TSMRemoteDebuggerThread(FRemoteDebuggerThread).startDebugCurrentThread(result);

  DoOnNewEngine(Result);
end;

function TSMEngine.DoProcessOperationCallback: Boolean;
begin
  Result := not fTimedOut;
end;

// Remove #13 characters from script(change it to #32)
// Spidermonkey debugger crashes when `...`(new ES6 strings) contains #13#10
................................................................................
  if r and isFirst and GlobalObject.ptr.HasProperty(cx, '_timerLoop') then
    r := GlobalObject.ptr.CallFunctionName(cx, '_timerLoop', 0, nil, rval);
  if not r then
    r := false;
  ScheduleWatchdog(-1);
  CheckJSError(r);
end;








function TSMEngine.EvaluateModule(const scriptName: RawUTF8): jsval;
var
  global: PJSRootedObject;
  moduleLoader: PJSRootedObject;
begin
  global := cx.NewRootedObject(cx.CurrentGlobalOrNull);






>







 







>
>







 







>







 







|
>
>
>







 







>
>











|







 







<




>







 







<
<
<







 







|







 







>
>
>
>







 







>
>
>
>
>







 







|


<
<








<
<
<


<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







>
>
>
>
>
>
>







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
..
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
...
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
...
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
...
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
...
361
362
363
364
365
366
367

368
369
370
371
372
373
374
375
376
377
378
379
...
392
393
394
395
396
397
398



399
400
401
402
403
404
405
...
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
...
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
....
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
....
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392


1393
1394
1395
1396
1397
1398
1399
1400



1401
1402

1403
1404
1405
1406
1407
1408
1409
....
1450
1451
1452
1453
1454
1455
1456














1457
1458
1459
1460
1461
1462
1463
....
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
  the Initial Developer. All Rights Reserved.

  Contributor(s):
  - Arnaud Bouchez
  - Vadim Orel
  - Pavel Mashlyakovsky
  - win2014
  - Geogre

  Alternatively, the contents of this file may be used under the terms of
  either the GNU General Public License Version 2 or later (the "GPL"), or
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  in which case the provisions of the GPL or the LGPL are applicable instead
  of those above. If you wish to allow use of your version of this file only
  under the terms of either the GPL or the LGPL, and not to allow others to
................................................................................
  - move a Worker classes from UnityBase to SyNode
    Allow to create and communicate with a threads from JavaScript
  - implement nodeJS Buffer
  - improved compartibility with nodeJS utils
  - add a `process.version`
  - implement a setTimeout/setInverval/setImmediate etc.
  - SpiderMonkey 52 (x32 & x64) support ( SM52 condition should be defined )
  - VSCode debugging support via [vscode-firefox-debug](https://github.com/hbenl/vscode-firefox-debug) adapter
    Thanks to George for patch

}

{$I Synopse.inc} // define HASINLINE USETYPEINFO CPU32 CPU64 OWNNORMTOUPPER
{$I SyNode.inc}   // define SM_DEBUG CONSIDER_TIME_IN_Z

interface
................................................................................
    fLastErrorFileName: RawUTF8;
    fLastErrorLine: integer;
    fLastErrorStackTrace: SynUnicode;
    fErrorExist: boolean;
    fThreadData: pointer;
    fDllModulesUnInitProcList: TList;
    fNeverExpire: boolean;
    fWebAppRootDir: RawUTF8;
    /// called from SpiderMonkey callback. Do not raise exception here
    // instead use CheckJSError metod after JSAPI compile/evaluate call
{$IFDEF SM52}
    procedure DoProcessJSError(report: PJSErrorReport); virtual;
{$ELSE}
    procedure DoProcessJSError(errMsg: PCChar; report: PJSErrorReport); virtual;
{$ENDIF}
................................................................................
    /// evaluate a JavaScript script in the global scope
    // - if exception raised in script - raise Delphi ESMException
    // - on success returns last executed expression statement processed
    // in the script as a variant
    // - JavaScript equivalent to
    // ! eval(script)
    procedure Evaluate(const script: SynUnicode; const scriptName: RawUTF8;
      lineNo: Cardinal; out result: jsval); overload;

    procedure Evaluate(const script: SynUnicode; const scriptName: RawUTF8;
      lineNo: Cardinal); overload;

    /// evaluate a JavaScript script as Module
    // - if exception raised in script - raise Delphi ESMException
    // - JavaScript equivalent to import of ES6
    function EvaluateModule(const scriptName: RawUTF8): jsval;

    /// run method in object
................................................................................
    procedure DefineNodeProcess;
    procedure DefineModuleLoader;

    /// Used by debugger
    property PrivateDataForDebugger: Pointer read FPrivateDataForDebugger write SetPrivateDataForDebugger;
    /// Name of this engine. Will be shown in debugger for indentify this engine
    property nameForDebug: RawUTF8 read fnameForDebug;
    /// root path for current web app engine context
    property webAppRootDir: RawUTF8 read fWebAppRootDir;
    /// Handler hor debugger. Called from debugger thread when debugger need
    // call rt.InterruptCallback(cx) in engine''s thread
    // this method must wake up the engine's thread and thread must
    // execute rt.InterruptCallback(cx) for this engine
    property doInteruptInOwnThread: TThreadMethod read fDoInteruptInOwnThread write fDoInteruptInOwnThread;
  end;


  /// prototype of SpideMonkey notification callback method
  TEngineEvent = procedure(const Engine: TSMEngine) of object;

  /// event of getting engine name or web app root path
  TEngineNameEvent = function(const Engine: TSMEngine): RawUTF8 of object;

  /// event for loading dll module
  TDllModuleEvent = procedure(Handle: HMODULE) of object;

  /// event for adding values to process.binding
  TSMProcessBindingHandler = function(const Engine: TSMEngine; const bindingNamespaceName: SynUnicode): jsval;
................................................................................
  TSMEngineManager = class
  private
    FMaxPerEngineMemory: Cardinal;
    FMaxNurseryBytes: Cardinal;
    FMaxRecursionDepth: Cardinal;
    FEnginePool: TObjectListLocked;
    FRemoteDebuggerThread: TThread;

    FContentVersion: Cardinal;
    FOnNewEngine: TEngineEvent;
    FOnDebuggerInit: TEngineEvent;
    FOnGetName: TEngineNameEvent;
    FOnGetWebAppRootPath: TEngineNameEvent;
    FOnDebuggerConnected: TEngineEvent;
    FOnDllModuleLoaded: TDllModuleEvent;
    {$ifdef ISDELPHIXE2}
    FRttiCx: TRttiContext;
    {$endif}
    FEngineClass: TSMEngineClass;

................................................................................
    /// Release a javaScript engine for specified thread
    procedure ReleaseEngineForThread(aThreadID: DWORD);
    /// returns -1 if none was defined yet
    // - this method is not protected via the global FEngineCS mutex/lock
    function ThreadEngineIndex(ThreadID: DWORD): Integer;
    /// returns nil if none was defined yet
    function CurrentThreadEngine: TSMEngine;



    /// called when a new Engine is created
    // - this default implementation will run the OnNewEngine callback (if any)
    procedure DoOnNewEngine(const Engine: TSMEngine); virtual;

    function getPauseDebuggerOnFirstStep: boolean;
    procedure setPauseDebuggerOnFirstStep(const Value: boolean);

................................................................................
    {$endif}
    /// Start/stop debugger listening on selected port
    // - expects the port to be specified as Ansi string, e.g. '1234'
    // - you can optionally specify a server address to bind to, e.g.
    // '1.2.3.4:1234'
    // - debugger create a dedicated thread where it listen to a requests
    // from a remote debug UI
    procedure startDebugger(const port: SockString = '6000');
    procedure stopDebugger;
    /// Write text as console log to current thread Engine's debugger (if exists)
    procedure debuggerLog(const Text: RawUTF8);
    /// when debugger connected to new engine this Engine must pause on first step
    property pauseDebuggerOnFirstStep: boolean read getPauseDebuggerOnFirstStep write setPauseDebuggerOnFirstStep;
    /// Workers manager
    property WorkersManager: TJSWorkersManager read FWorkersManager;
................................................................................
      read GetEngineExpireTimeOutMinutes write SetEngineExpireTimeOutMinutes default 0;
    /// Path to core synode modules
    property CoreModulesPath: RawUTF8 read FCoreModulesPath;
    /// event triggered every time a new Engine is created
    // event trigered before OnDebuggerInit and OnNewEngine events
    // Result og this method is Engine's name for debug
    property OnGetName: TEngineNameEvent read FOnGetName write FOnGetName;
    /// event triggered every time a new Engine is created
    // event trigered before OnDebuggerInit and OnNewEngine events
    // Result og this method is Engine's web app root path
    property OnGetWebAppRootPath: TEngineNameEvent read FOnGetWebAppRootPath write FOnGetWebAppRootPath;

    /// event triggered every time a internal debugger process connected to Engine
    // - event trigered in debugger's compartment
    // - here your code can change the initial state of the debugger
    property OnDebuggerInit: TEngineEvent read FOnDebuggerInit write FOnDebuggerInit;

    /// event triggered every time a new Engine is created
................................................................................
      Result.SetThreadData(pThreadData);

    if WorkersManager.curThreadIsWorker then
      Result.fnameForDebug := WorkersManager.getCurrentWorkerThreadName
    else if Assigned(OnGetName) then
      Result.fnameForDebug := OnGetName(Result);

    if Assigned(OnGetWebAppRootPath) then
      Result.fWebAppRootDir := OnGetWebAppRootPath(Result)
    else
      Result.fWebAppRootDir := UnicodeStringToUtf8(ExeVersion.ProgramFilePath);

    result.fThreadID := ThreadID;
    FEnginePool.Add(result);
  finally
    FEnginePool.Safe.UnLock;
  end;

  if FRemoteDebuggerThread <> nil then
................................................................................
procedure TSMEngineManager.setPauseDebuggerOnFirstStep(const Value: boolean);
begin
  if FRemoteDebuggerThread<> nil then begin
    TSMRemoteDebuggerThread(FRemoteDebuggerThread).NeedPauseOnFirstStep := Value;
  end
end;

procedure TSMEngineManager.startDebugger(const port: SockString = '6000');
begin
  FRemoteDebuggerThread := TSMRemoteDebuggerThread.Create(self, port);


  inc(FContentVersion);
end;

procedure TSMEngineManager.stopDebugger;
begin
  if FRemoteDebuggerThread <> nil then begin
    TSMRemoteDebuggerThread(FRemoteDebuggerThread).SetTerminated;
    FRemoteDebuggerThread := nil;



    end;
  end;


function StringReplaceChars(const Source: String; OldChar, NewChar: Char): String;
var i,j,n: integer;
begin
  if (OldChar<>NewChar) and (Source<>'') then begin
    n := length(Source);
    for i := 0 to n-1 do
................................................................................

  if ACheckResultInsideBase and
     ((length(Result) < length(ABaseDir)) or (length(ABaseDir)=0) or
      (StrLIComp(PChar(@Result[1]), @localBase[1], length(localBase)) <> 0)) then
    Result := ''
end;















function TSMEngine.DoProcessOperationCallback: Boolean;
begin
  Result := not fTimedOut;
end;

// Remove #13 characters from script(change it to #32)
// Spidermonkey debugger crashes when `...`(new ES6 strings) contains #13#10
................................................................................
  if r and isFirst and GlobalObject.ptr.HasProperty(cx, '_timerLoop') then
    r := GlobalObject.ptr.CallFunctionName(cx, '_timerLoop', 0, nil, rval);
  if not r then
    r := false;
  ScheduleWatchdog(-1);
  CheckJSError(r);
end;

procedure TSMEngine.Evaluate(const script: SynUnicode; const scriptName: RawUTF8; lineNo: Cardinal);
var
  jsvalue: jsval;
begin
  Evaluate(script, scriptName, lineNo, jsvalue);
end;

function TSMEngine.EvaluateModule(const scriptName: RawUTF8): jsval;
var
  global: PJSRootedObject;
  moduleLoader: PJSRootedObject;
begin
  global := cx.NewRootedObject(cx.CurrentGlobalOrNull);

Changes to SyNode/SyNodeBinding_fs.pas.

302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
end;

/// read content of directory. return array of file names witout started from dot '.'
/// in case of includeDirNames - return sub direcrory with trailing slash
function fs_readDir(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
var
  in_argv: PjsvalVector;
  aValue: jsval;
  dir, founded: TFileName;
  F: TSearchRec;
  res: PJSRootedObject;
  cNum, searchAttr: integer;
  includeSubDir: boolean;
const
  USAGE = 'usage: readDir(dirPath: String; [includeDirNames: boolean = false]): Array';






<







302
303
304
305
306
307
308

309
310
311
312
313
314
315
end;

/// read content of directory. return array of file names witout started from dot '.'
/// in case of includeDirNames - return sub direcrory with trailing slash
function fs_readDir(cx: PJSContext; argc: uintN; var vp: jsargRec): Boolean; cdecl;
var
  in_argv: PjsvalVector;

  dir, founded: TFileName;
  F: TSearchRec;
  res: PJSRootedObject;
  cNum, searchAttr: integer;
  includeSubDir: boolean;
const
  USAGE = 'usage: readDir(dirPath: String; [includeDirNames: boolean = false]): Array';

Changes to SyNode/SyNodeRemoteDebugger.pas.

33
34
35
36
37
38
39

40
41
42
43
44
45
46
...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
...
160
161
162
163
164
165
166


167
168
169
170
171
172
173
...
447
448
449
450
451
452
453

454
455






456
457


458
459

460
461


462
463
464
465
466
467
468
...
607
608
609
610
611
612
613


614
615
616
617
618
619
620
...
688
689
690
691
692
693
694


695

696
697
698
699
700
701
702
...
799
800
801
802
803
804
805






































806
807
808
809
810
811
812
...
816
817
818
819
820
821
822




823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
  the Initial Developer. All Rights Reserved.

  Contributor(s):
  - Arnaud Bouchez
  - Vadim Orel
  - Pavel Mashlyakovsky
  - win2014


  Alternatively, the contents of this file may be used under the terms of
  either the GNU General Public License Version 2 or later (the "GPL"), or
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  in which case the provisions of the GPL or the LGPL are applicable instead
  of those above. If you wish to allow use of your version of this file only
  under the terms of either the GPL or the LGPL, and not to allow others to
................................................................................
    procedure stopDebugCurrentThread(aEng: TSMEngine);
    /// Write log to current thread engine
    procedure doLog(const Text: RawUTF8);

    property NeedPauseOnFirstStep: boolean read FNeedPauseOnFirstStep write FNeedPauseOnFirstStep;
  end;

  /// Fake class required by VSCode FireFox Debug extension
  // https://github.com/hbenl/vscode-firefox-debug
  // SyNode engine expose themself as a FireFox addons, in this case
  // vscode-firefox-debug tyr to "install" a addon by senfing a commands to port 8888
  // In this sace we do nothing - all out sources are already inside engine
  TFakeXPIInstaller = class(TThread)
  private
    fPort: SockString;
  protected
    procedure Execute; override;
  public
    procedure SetTerminated;
    constructor Create(const aPort: SockString = '8888');
  end;

  function SyNodeBindingProc_debugger(const Engine: TSMEngine; const bindingNamespaceName: SynUnicode): jsval;

implementation
uses
  {$ifdef MSWINDOWS} Windows, {$endif}
  SynWinSock, SysUtils;

................................................................................
    {$IFNDEF SM52}
    fOldInterruptCallback: JSInterruptCallback;
    {$ENDIF}
    fSmThreadID: TThreadID;
    fNameForDebug: RawUTF8;
    fCommunicationThread: TSMRemoteDebuggerCommunicationThread;
    fIsJustInited: boolean;


    /// Debugger create his own compartmnet (his own global object & scripting context)
    // Here we initialize a new compartment
    procedure InitializeDebuggerCompartment(aEng: TSMEngine; aNeedPauseOnFirstStep: boolean);
  public
    constructor Create(aParent: TSMRemoteDebuggerThread; aEng: TSMEngine);
    destructor Destroy; override;
    procedure Send(const packet: RawUTF8);
................................................................................
      if request.type = 'listAddons' then begin
        Writer.AddShort('{"from":"root","addons":[');
        fParent.fDebuggers.Safe.Lock;
        try
          for I := 0 to fParent.fDebuggers.Count - 1 do begin
            engine := fParent.fManager.EngineForThread(TSMDebugger(fParent.fDebuggers[i]).fSmThreadID);
            if engine <> nil then begin

              Writer.AddShort('{"actor":"server1.conn1.addon');
              Writer.Add(TSMDebugger(fParent.fDebuggers[i]).fIndex);






              Writer.AddShort('","id":"server1.conn1.addon');
              Writer.Add(TSMDebugger(fParent.fDebuggers[i]).fIndex);


              Writer.AddShort('","name":"');
              Writer.AddString(TSMDebugger(fParent.fDebuggers[i]).fNameForDebug);

              Writer.AddShort('","url":"server1.conn1.addon');
              Writer.Add(TSMDebugger(fParent.fDebuggers[i]).fIndex);


              Writer.AddShort('","debuggable":');
              Writer.Add(TSMDebugger(fParent.fDebuggers[i]).fCommunicationThread = nil);
              Writer.AddShort(',"consoleActor":"console');
              Writer.Add(TSMDebugger(fParent.fDebuggers[i]).fIndex);
              Writer.AddShort('"},');
            end;
          end;
................................................................................
  inc(aParent.fCurThreadIndex);

  fSmThreadID := GetCurrentThreadId;

  fMessagesQueue := TRawUTF8ListHashedLocked.Create();
  fLogQueue := TRawUTF8ListHashedLocked.Create();
  fNameForDebug := aEng.nameForDebug;



  InitializeDebuggerCompartment(aEng, aParent.FNeedPauseOnFirstStep);
end;

destructor TSMDebugger.Destroy;
begin
  if fCommunicationThread <> nil then
................................................................................
      Assert(cx.DefineDebuggerObject(aEng.GlobalObjectDbg.ptr));
      Assert(cx.InitModuleClasses(aEng.GlobalObjectDbg.ptr));
      aEng.DefineProcessBinding;
      aEng.DefineModuleLoader;
      aEng.EvaluateModule('DevTools\Debugger.js');
      dbgObject := cx.NewRootedObject(aEng.GlobalObjectDbg.ptr.GetPropValue(cx, 'process').asObject.GetPropValue(cx, 'dbg').asObject);
      try


        aEng.CallObjectFunction(dbgObject, 'init', [SimpleVariantToJSval(cx, fIndex), SimpleVariantToJSval(cx, aNeedPauseOnFirstStep)]);

      finally
        cx.FreeRootedObject(dbgObject);
      end;

      if Assigned(aEng.Manager.OnDebuggerInit) then
        aEng.Manager.OnDebuggerInit(aEng);

................................................................................
var
  debugger: TSMDebugger;
begin
  debugger := TSMEngine(cx.PrivateData).PrivateDataForDebugger;
  vp.rval := SimpleVariantToJSval(cx, debugger.fIsPaused);
  result := true;
end;







































function SyNodeBindingProc_debugger(const Engine: TSMEngine;
  const bindingNamespaceName: SynUnicode): jsval;
var
  obj: PJSRootedObject;
  cx: PJSContext;
begin
................................................................................
    Assert(cx.WrapObject(Engine.GlobalObject.ptr));

    obj.ptr.DefineFunction(cx, 'send', debugger_send, 1);
    obj.ptr.DefineFunction(cx, 'logError', debugger_err, 1);
    obj.ptr.DefineFunction(cx, 'read', debugger_read, 0);
    obj.ptr.DefineProperty(cx, 'listen', JSVAL_NULL, 0, debugger_listen);
    obj.ptr.DefineProperty(cx, 'paused', JSVAL_NULL, 0, debugger_isPaused, debugger_setPaused);





    obj.ptr.DefineProperty(cx, 'global', Engine.GlobalObject.ptr.ToJSValue);

    Result := obj.ptr.ToJSValue;
  finally
    cx.FreeRootedObject(obj);
  end;

end;

{ TFakeXPIInstaller }

constructor TFakeXPIInstaller.Create(const aPort: SockString);
begin
  fPort := aPort;
  FreeOnTerminate := true;
  inherited Create(False);
end;

procedure TFakeXPIInstaller.Execute;
var
  ServerSock: TCrtSocket;
  ClientSin: TVarSin;
  AcceptedSocket: TSocket;
begin
  ServerSock := TCrtSocket.Bind(fPort);
  try
    repeat
      AcceptedSocket := Accept(ServerSock.Sock, ClientSin);
    until Terminated;
  finally
    ServerSock.Free;
  end;
end;

procedure TFakeXPIInstaller.SetTerminated;
var
  socket: TCrtSocket;
begin
  Terminate;
  socket := Open('127.0.0.1', fPort);
  if socket<>nil then
    socket.Free;
end;

initialization
  TSMEngineManager.RegisterBinding('debugger', SyNodeBindingProc_debugger);

end.







>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







>
>







 







>
|
|
>
>
>
>
>
>
|
|
>
>


>
|
|
>
>







 







>
>







 







>
>
|
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>










|

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
...
103
104
105
106
107
108
109















110
111
112
113
114
115
116
...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
...
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
...
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
...
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
...
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
...
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881


































882
883
884
885
  the Initial Developer. All Rights Reserved.

  Contributor(s):
  - Arnaud Bouchez
  - Vadim Orel
  - Pavel Mashlyakovsky
  - win2014
  - George

  Alternatively, the contents of this file may be used under the terms of
  either the GNU General Public License Version 2 or later (the "GPL"), or
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  in which case the provisions of the GPL or the LGPL are applicable instead
  of those above. If you wish to allow use of your version of this file only
  under the terms of either the GPL or the LGPL, and not to allow others to
................................................................................
    procedure stopDebugCurrentThread(aEng: TSMEngine);
    /// Write log to current thread engine
    procedure doLog(const Text: RawUTF8);

    property NeedPauseOnFirstStep: boolean read FNeedPauseOnFirstStep write FNeedPauseOnFirstStep;
  end;
















  function SyNodeBindingProc_debugger(const Engine: TSMEngine; const bindingNamespaceName: SynUnicode): jsval;

implementation
uses
  {$ifdef MSWINDOWS} Windows, {$endif}
  SynWinSock, SysUtils;

................................................................................
    {$IFNDEF SM52}
    fOldInterruptCallback: JSInterruptCallback;
    {$ENDIF}
    fSmThreadID: TThreadID;
    fNameForDebug: RawUTF8;
    fCommunicationThread: TSMRemoteDebuggerCommunicationThread;
    fIsJustInited: boolean;
    fDebuggerName: RawUTF8;
    fWebAppRootPath: RawUTF8;
    /// Debugger create his own compartmnet (his own global object & scripting context)
    // Here we initialize a new compartment
    procedure InitializeDebuggerCompartment(aEng: TSMEngine; aNeedPauseOnFirstStep: boolean);
  public
    constructor Create(aParent: TSMRemoteDebuggerThread; aEng: TSMEngine);
    destructor Destroy; override;
    procedure Send(const packet: RawUTF8);
................................................................................
      if request.type = 'listAddons' then begin
        Writer.AddShort('{"from":"root","addons":[');
        fParent.fDebuggers.Safe.Lock;
        try
          for I := 0 to fParent.fDebuggers.Count - 1 do begin
            engine := fParent.fManager.EngineForThread(TSMDebugger(fParent.fDebuggers[i]).fSmThreadID);
            if engine <> nil then begin
              // Actor represent debug thread here, setting proper name with coxtext thread id
              // Writer.AddShort('{"actor":"server1.conn1.addon');
              // Writer.Add(TSMDebugger(fParent.fDebuggers[i]).fIndex);
              Writer.AddShort('{"actor":"');
              Writer.AddShort(TSMDebugger(fParent.fDebuggers[i]).fDebuggerName);
              Writer.AddShort('.conn1.thread_');
              { TODO : check that in multithread mode this field equal thread id with js context that we debug, otherwire replace with proper assigment }
              Writer.AddU(TSMDebugger(fParent.fDebuggers[i]).fSmThreadID);
              // id should be addon id, value from DoOnGetEngineName event
              // Writer.AddShort('","id":"server1.conn1.addon');
              // Writer.Add(TSMDebugger(fParent.fDebuggers[i]).fIndex);
              Writer.AddShort('","id":"');
              Writer.AddString(TSMDebugger(fParent.fDebuggers[i]).fNameForDebug);
              Writer.AddShort('","name":"');
              Writer.AddString(TSMDebugger(fParent.fDebuggers[i]).fNameForDebug);
              // url most likly should be addon folder in format: file:///drive:/path/
              // Writer.AddShort('","url":"server1.conn1.addon');
              // Writer.Add(TSMDebugger(fParent.fDebuggers[i]).fIndex);
              { TODO : replace with path generation, should be context home dir in format file:///drive:/path/ }
              Writer.AddShort('","url":"file:///' + StringReplaceAll(TSMDebugger(fParent.fDebuggers[i]).fWebAppRootPath, '\', '/'));
              Writer.AddShort('","debuggable":');
              Writer.Add(TSMDebugger(fParent.fDebuggers[i]).fCommunicationThread = nil);
              Writer.AddShort(',"consoleActor":"console');
              Writer.Add(TSMDebugger(fParent.fDebuggers[i]).fIndex);
              Writer.AddShort('"},');
            end;
          end;
................................................................................
  inc(aParent.fCurThreadIndex);

  fSmThreadID := GetCurrentThreadId;

  fMessagesQueue := TRawUTF8ListHashedLocked.Create();
  fLogQueue := TRawUTF8ListHashedLocked.Create();
  fNameForDebug := aEng.nameForDebug;
  fDebuggerName := 'synode_debPort_' + aParent.fPort;
  fWebAppRootPath := aEng.webAppRootDir;

  InitializeDebuggerCompartment(aEng, aParent.FNeedPauseOnFirstStep);
end;

destructor TSMDebugger.Destroy;
begin
  if fCommunicationThread <> nil then
................................................................................
      Assert(cx.DefineDebuggerObject(aEng.GlobalObjectDbg.ptr));
      Assert(cx.InitModuleClasses(aEng.GlobalObjectDbg.ptr));
      aEng.DefineProcessBinding;
      aEng.DefineModuleLoader;
      aEng.EvaluateModule('DevTools\Debugger.js');
      dbgObject := cx.NewRootedObject(aEng.GlobalObjectDbg.ptr.GetPropValue(cx, 'process').asObject.GetPropValue(cx, 'dbg').asObject);
      try
        aEng.CallObjectFunction(dbgObject, 'init', [
          SimpleVariantToJSval(cx, fIndex),
          SimpleVariantToJSval(cx, aNeedPauseOnFirstStep)
          ]);
      finally
        cx.FreeRootedObject(dbgObject);
      end;

      if Assigned(aEng.Manager.OnDebuggerInit) then
        aEng.Manager.OnDebuggerInit(aEng);

................................................................................
var
  debugger: TSMDebugger;
begin
  debugger := TSMEngine(cx.PrivateData).PrivateDataForDebugger;
  vp.rval := SimpleVariantToJSval(cx, debugger.fIsPaused);
  result := true;
end;

function debugger_debuggerName(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean; cdecl;
var
  debugger: TSMDebugger;
begin
  debugger := TSMEngine(cx.PrivateData).PrivateDataForDebugger;
  vp.rval := SimpleVariantToJSval(cx, debugger.fDebuggerName);
  result := true;
end;

function debugger_nameForDebug(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean; cdecl;
var
  debugger: TSMDebugger;
begin
  debugger := TSMEngine(cx.PrivateData).PrivateDataForDebugger;
  vp.rval := SimpleVariantToJSval(cx, debugger.fNameForDebug);
  result := true;
end;

function debugger_threadId(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean; cdecl;
var
  debugger: TSMDebugger;
begin
  debugger := TSMEngine(cx.PrivateData).PrivateDataForDebugger;
  { TODO : check that in multithread mode this field equal thread id with js context that we debug, otherwire replace with proper assigment }  
  vp.rval := SimpleVariantToJSval(cx, ToUTF8(debugger.fSmThreadID));
  // TSMDebugger(fParent.fDebuggers[i]).fSmThreadID
  result := true;
end;

function debugger_webAppRootPath(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean; cdecl;
var
  debugger: TSMDebugger;
begin
  debugger := TSMEngine(cx.PrivateData).PrivateDataForDebugger;
  vp.rval := SimpleVariantToJSval(cx, debugger.fWebAppRootPath);
  result := true;
end;

function SyNodeBindingProc_debugger(const Engine: TSMEngine;
  const bindingNamespaceName: SynUnicode): jsval;
var
  obj: PJSRootedObject;
  cx: PJSContext;
begin
................................................................................
    Assert(cx.WrapObject(Engine.GlobalObject.ptr));

    obj.ptr.DefineFunction(cx, 'send', debugger_send, 1);
    obj.ptr.DefineFunction(cx, 'logError', debugger_err, 1);
    obj.ptr.DefineFunction(cx, 'read', debugger_read, 0);
    obj.ptr.DefineProperty(cx, 'listen', JSVAL_NULL, 0, debugger_listen);
    obj.ptr.DefineProperty(cx, 'paused', JSVAL_NULL, 0, debugger_isPaused, debugger_setPaused);
    obj.ptr.DefineProperty(cx, 'debuggerName', JSVAL_NULL, 0, debugger_debuggerName);
    obj.ptr.DefineProperty(cx, 'addonID', JSVAL_NULL, 0, debugger_nameForDebug);
    obj.ptr.DefineProperty(cx, 'threadId', JSVAL_NULL, 0, debugger_threadId);
    obj.ptr.DefineProperty(cx, 'webAppRootPath', JSVAL_NULL, 0, debugger_webAppRootPath);

    obj.ptr.DefineProperty(cx, 'global', Engine.GlobalObject.ptr.ToJSValue);

    Result := obj.ptr.ToJSValue;
  finally
    cx.FreeRootedObject(obj);
  end;

end;

initialization



































  TSMEngineManager.RegisterBinding('debugger', SyNodeBindingProc_debugger);

end.

Changes to SyNode/core_modules/DevTools/Debugger.js.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
906
907
908
909
910
911
912




















913
914
915
916
917
918
919
920
921
922

923
924
925








926
927
928
929
930
931
932
933
934
935

936
937
938
939
940
941
942
....
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
....
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
class ActorManager {
    constructor(threadID) {
        this.threadID = threadID;
    }
    init(){
        this.console = new ConsoleActor('console' + this.threadID);
        this.addon = new AddonActor('addon' + this.threadID);
    }
    getActor(actorName){
        let actor = this;
        actorName.split('.').forEach(function(elem){
            if (actor)
                actor = actor[elem];
        });
................................................................................
            let actor = this[line] ? this[line] : new BreakpointActor(this, line, entryPoints);
            actor._condition = condition;
            return actor;
        } else {
            return null;
        }
    }




















    get _resp(){
        let source = this._source;// || this.generatedSource;
        // This might not have a source or a generatedSource because we
        // treat HTML pages with inline scripts as a special SourceActor
        // that doesn't have either
        let introductionUrl = null;
        if (source && source.introductionScript) {
            introductionUrl = source.introductionScript.source.url;
        }


        let url = this._source.url;
        if (url === 'debugger eval code') {
            url = null;








        }
        return {
            "actor": this.fullActor,
            "url": url ? url.split(" -> ").pop() : null,
            "addonPath" : (url && url.lastIndexOf('\\') > 0) ? url.substr(url.lastIndexOf('\\')+1) : url, // displayed as a file name
            "addonID": url ? ((url.lastIndexOf('\\') > 0) ? url.substr(0, url.lastIndexOf('\\') ) : '<>') : null, //displayed as a folder in debugger
            "isBlackBoxed": false,
            "isPrettyPrinted": false,
            "introductionUrl": introductionUrl ? introductionUrl.split(" -> ").pop() : null,
            introductionType: source ? source.introductionType : null

        }
    }
}

class BreakpointActor extends Actor{
    constructor (sourceActor, lineNo, entryPoints) {
        super(lineNo, sourceActor);
................................................................................

        return this._frame.arguments.map(arg => this.getGrip(arg));
    }
}

class AddonActor extends Actor {
    constructor(actorName) {
        let serverActor = new Actor('server1'),
            connActor = new Actor('conn1', serverActor);
        super(actorName, connActor);
        new ThreadActor();
    }
    attach(aRequest){
        return {
            "type": "tabAttached",
................................................................................
        inRequest = typeof(msg) === "string" ? JSON.parse(msg) : msg;
        actorName = inRequest.to;
        actor = actorManager.getActor(actorName);
        if (actor) {
            handler = actor[inRequest.type];
            outRequest = handler ? actor[inRequest.type](inRequest) : {};
            if (!handler)
                DevToolsUtils.reportException('newMessage: ' + inRequest.type + ' not found in ' + actorName, msg);
            if (outRequest) {
                outRequest.from = actorName;
                dbg_binding.send(outRequest);
            } else {
                DevToolsUtils.reportException('newMessage: ' + actorName + '.' + inRequest.type + '! outRequest', msg);
            }
        } else {






|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>









<
>

|
|
>
>
>
>
>
>
>
>



|
|
|


|
|
>







 







|







 







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941

942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
....
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
....
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
class ActorManager {
    constructor(threadID) {
        this.threadID = threadID;
    }
    init(){
        this.console = new ConsoleActor('console' + this.threadID);
        this.addon = new AddonActor('thread_' + dbg_binding.threadId);
    }
    getActor(actorName){
        let actor = this;
        actorName.split('.').forEach(function(elem){
            if (actor)
                actor = actor[elem];
        });
................................................................................
            let actor = this[line] ? this[line] : new BreakpointActor(this, line, entryPoints);
            actor._condition = condition;
            return actor;
        } else {
            return null;
        }
    }
    getProperPath(p) {
        let res = p;
        // replace single backslash with url slash (every occurrence)
        res = res.replace(/\\/g, '/');
        if (res.startsWith('//')) {
            res = 'file:' + res
        }
        else if (res.charAt(1) == ':') {
            res = 'file:///' + res
        } 
        return res;
    }
    findSourceMapData(data) {
        let result = undefined;
        let searchResult = new RegExp('\/\/# ?sourceMappingURL ?= ?(.*)', 'i').exec(data);
        if (searchResult) {
            result = searchResult.pop();
        }
        return result;
    }
    get _resp(){
        let source = this._source;// || this.generatedSource;
        // This might not have a source or a generatedSource because we
        // treat HTML pages with inline scripts as a special SourceActor
        // that doesn't have either
        let introductionUrl = null;
        if (source && source.introductionScript) {
            introductionUrl = source.introductionScript.source.url;
        }

        let addonPath = undefined;
        let url = this._source.url;
        if (!url || url === 'debugger eval code') {
            url = undefined;
        } else {
            url = this.getProperPath(url.split(" -> ").pop());
            if (url.startsWith('file:')) {
                let webAppRootPath = this.getProperPath(dbg_binding.webAppRootPath);
                if (url.startsWith(webAppRootPath)) {
                    addonPath = url.substr(webAppRootPath.length);
                }
            }
        }
        return {
            "actor": this.fullActor,
            "url": url,
            "addonPath" : addonPath,
            "addonID": addonPath ? dbg_binding.addonID : undefined,
            "isBlackBoxed": false,
            "isPrettyPrinted": false,
            "introductionUrl": introductionUrl ? introductionUrl.split(' -> ').pop() : undefined,
            introductionType: source ? source.introductionType : '',
            "sourceMapURL": this.findSourceMapData(source.text)
        }
    }
}

class BreakpointActor extends Actor{
    constructor (sourceActor, lineNo, entryPoints) {
        super(lineNo, sourceActor);
................................................................................

        return this._frame.arguments.map(arg => this.getGrip(arg));
    }
}

class AddonActor extends Actor {
    constructor(actorName) {
        let serverActor = new Actor(dbg_binding.debuggerName),
            connActor = new Actor('conn1', serverActor);
        super(actorName, connActor);
        new ThreadActor();
    }
    attach(aRequest){
        return {
            "type": "tabAttached",
................................................................................
        inRequest = typeof(msg) === "string" ? JSON.parse(msg) : msg;
        actorName = inRequest.to;
        actor = actorManager.getActor(actorName);
        if (actor) {
            handler = actor[inRequest.type];
            outRequest = handler ? actor[inRequest.type](inRequest) : {};
            if (!handler)
                DevToolsUtils.reportException('newMessage: ' + inRequest.type + ' not found in ' + actorName + ' Class ' + actor.constructor.name, msg);
            if (outRequest) {
                outRequest.from = actorName;
                dbg_binding.send(outRequest);
            } else {
                DevToolsUtils.reportException('newMessage: ' + actorName + '.' + inRequest.type + '! outRequest', msg);
            }
        } else {