-
Notifications
You must be signed in to change notification settings - Fork 267
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migrate to "node2" debug engine #390
Conversation
- target es6 syntax as Chrome debug client seems to be targeting es6 - check for app worker's existence before disposing - make sure packager port is known when handling attach request
- bail out properly - use real path of node2 debugger package
- use raw NodeDebugSession to report error - improve logging in app worker
@MSLaguana, @roblourens I would really appreciate any feedback on this |
src/debugger/forkedAppWorker.ts
Outdated
// tslint:disable-next-line:align | ||
const WORKER_BOOTSTRAP = ` | ||
// Hacks to avoid accessing unitialized variables | ||
var onmessage=null, self=global; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should you also reference importScripts
here?
src/debugger/forkedAppWorker.ts
Outdated
process.send(message); | ||
}; | ||
importScripts = function(scriptUrl){ | ||
var scriptCode = require("fs").readFileSync(scriptUrl, "utf8"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm cautious about these require
s here. I know that react-native redefines global require, so I'm not sure whether that can impact this if importScripts
is called more than once. Might be worth using an IIFE to take a reference to the original require function initially and use that when importScripts
is called? Or even just cache both fs
and vm
immediately and use those in importScripts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea! I totally missed that fact with require
override by react
// Before sending messages, make sure that the worker is loaded | ||
this.workerLoaded.promise | ||
.then(() => { | ||
if (rnMessage.method !== "executeApplicationScript") return Q.resolve(rnMessage); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't other messages be passed on to the other process to be run in there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see that is handled in the done
coming up next.
src/debugger/forkedAppWorker.ts
Outdated
importScripts = function(scriptUrl){ | ||
var scriptCode = require("fs").readFileSync(scriptUrl, "utf8"); | ||
require("vm").runInThisContext(scriptCode, { filename: scriptUrl }); | ||
};`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something else that I would recommend is adding a global __debug__
object with a require
function which caches the node require
. This matches what we currently expose in the original sandboxing method, and allows things such as realm to provide shims for native functionality when necessary.
LGTM. You may want to follow this issue - microsoft/vscode#19650 - as we're talking about how the two debug adapters will interact in the future. It probably won't mean anything for you, at least for February. There's a chance they could end up merged into a single extension or something, but probably not. |
On the whole this looks pretty good to me. Am I correct that the SingleLifetimeAppWorker is no longer used at all, and is replaced by the forking app worker? |
Thanks for review, guys!
That's correct. I initially thought that we might want to keep them both, just in case if we plan to support old "node" debugger, but this doesn't seem to be the case. Am i right? Do you want me to drop that old worker code? |
src/debugger/appWorker.ts
Outdated
function killWorker(worker: IDebuggeeWorker) { | ||
if (!worker) return; | ||
worker.stop(); | ||
worker = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't modify this.worker
which I believe was your intent.
Looks like one of our tests is failing now, but it seems to be specifically to do with the single lifetime worker which isn't as relevant any more. Could you please look into that and update or remove the test as appropriate? |
Also to answer your question about the single lifetime worker: I think it would be best to only have the one way of doing things, so I am inclined to remove the single lifetime worker and just focus on the forked worker. |
Worker script needs to be downloaded from packager and patched only once. However it was downloaded every time when new SingleLifetime worker was starting. Moving the download logic to MultipleLifetime worker would improve reload time. Also extract worker downloading logic for better testability
@MSLaguana, addressed your comments and updated tests to actually test current worker implementation. Please take a look once more |
src/test/debugger/appWorker.test.ts
Outdated
}); | ||
|
||
test("should be able to import scripts", function() { | ||
const scriptImportPath = "testScript.js"; | ||
// NOTE: we're not able to mock out |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this comment is unfinished?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, there should be a reasoning why we have to use real script file to test importScripts
. I'll update.
// We have not yet finished importing the script, we should not have posted a response yet | ||
assert(postReplyFunction.notCalled, "postReplyFuncton called before scripts imported"); | ||
scriptImportDeferred.resolve(void 0); | ||
return Q.delay(1); | ||
return Q.delay(100); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reasoning behind increasing this limit here? Before we used 1 just to make sure that it was asynchronous. Is the delay of 100ms actually required for processing to happen on time, are there potential race conditions here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that previously the timeout was required just to delay dispatching the current promise chain and let scriptImportDeferred
chain to be dispatched first.
In this implementation the spawned process is a black box and completely async for us so we can't reliably determine when the script has been imported and executed, so the only way to test this IMO is to give some reasonable amount of time to worker to complete script's import before checking if it has been done.
As for race conditions - not sure where are they might happen. Every test spawns it's own worker, so they are isolated from each other.
Just a couple of questions, looking good. |
Looks great, thanks @vladimir-kotikov! |
Use "node2" debugging engine (https://github.com/Microsoft/vscode-node-debug2) rather than "node" for debugging react-native code. As mentioned in #377 previous approach to debugging will be deprecated in Node 8.
This should resolve a lot of debugging issues we've seen previously, such as #367 and #356