From 5f2f1f170fd4fdacdeb18b4b5e66c2ab14705db4 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Wed, 6 Jan 2016 00:21:16 +0300 Subject: [PATCH 01/23] Update DebuggerScript from Blink/8d2d750 --- InjectedScript/DebuggerScript.js | 126 ++++++++++++++++++------------- 1 file changed, 73 insertions(+), 53 deletions(-) diff --git a/InjectedScript/DebuggerScript.js b/InjectedScript/DebuggerScript.js index 0bdef39..22483ed 100644 --- a/InjectedScript/DebuggerScript.js +++ b/InjectedScript/DebuggerScript.js @@ -54,21 +54,6 @@ DebuggerScript.getAfterCompileScript = function(eventData) return DebuggerScript._formatScript(eventData.script_.script_); } -DebuggerScript.getWorkerScripts = function() -{ - var result = []; - var scripts = Debug.scripts(); - for (var i = 0; i < scripts.length; ++i) { - var script = scripts[i]; - // Workers don't share same V8 heap now so there is no need to complicate stuff with - // the context id like we do to discriminate between scripts from different pages. - // However we need to filter out v8 native scripts. - if (script.context_data && script.context_data === "worker") - result.push(DebuggerScript._formatScript(script)); - } - return result; -} - DebuggerScript.getFunctionScopes = function(fun) { var mirror = MakeMirror(fun); @@ -89,6 +74,31 @@ DebuggerScript.getFunctionScopes = function(fun) return result; } +DebuggerScript.getGeneratorObjectDetails = function(object) +{ + var mirror = MakeMirror(object, true /* transient */); + if (!mirror.isGenerator()) + return null; + var funcMirror = mirror.func(); + if (!funcMirror.resolved()) + return null; + var result = { + "function": funcMirror.value(), + "functionName": DebuggerScript._displayFunctionName(funcMirror) || "", + "status": mirror.status() + }; + var script = funcMirror.script(); + var location = mirror.sourceLocation() || funcMirror.sourceLocation(); + if (script && location) { + result["location"] = { + "scriptId": String(script.id()), + "lineNumber": location.line, + "columnNumber": location.column + }; + } + return result; +} + DebuggerScript.getCollectionEntries = function(object) { var mirror = MakeMirror(object, true /* transient */); @@ -103,20 +113,6 @@ DebuggerScript.getCollectionEntries = function(object) } } -DebuggerScript.getInternalProperties = function(value) -{ - var properties = ObjectMirror.GetInternalProperties(value); - var result = []; - for (var i = 0; i < properties.length; i++) { - var mirror = properties[i]; - result.push({ - name: mirror.name(), - value: mirror.value().value() - }); - } - return result; -} - DebuggerScript.setFunctionVariableValue = function(functionValue, scopeIndex, variableName, newValue) { var mirror = MakeMirror(functionValue); @@ -134,24 +130,24 @@ DebuggerScript._setScopeVariableValue = function(scopeHolder, scopeIndex, variab return undefined; } -DebuggerScript.getScripts = function(contextData) +DebuggerScript.getScripts = function(contextGroupId) { var result = []; - - if (!contextData) - return result; - var comma = contextData.indexOf(","); - if (comma === -1) - return result; - // Context data is a string in the following format: - // ("page"|"injected")"," - var idSuffix = contextData.substring(comma); // including the comma - var scripts = Debug.scripts(); + var contextDataPrefix = null; + if (contextGroupId) + contextDataPrefix = contextGroupId + ","; for (var i = 0; i < scripts.length; ++i) { var script = scripts[i]; - if (script.context_data && script.context_data.lastIndexOf(idSuffix) != -1) - result.push(DebuggerScript._formatScript(script)); + if (contextDataPrefix) { + if (!script.context_data) + continue; + // Context data is a string in the following format: + // ","("page"|"injected"|"worker") + if (script.context_data.indexOf(contextDataPrefix) !== 0) + continue; + } + result.push(DebuggerScript._formatScript(script)); } return result; } @@ -183,7 +179,8 @@ DebuggerScript._formatScript = function(script) startColumn: script.column_offset, endLine: endLine, endColumn: endColumn, - isContentScript: !!script.context_data && script.context_data.indexOf("injected") == 0 + isContentScript: !!script.context_data && script.context_data.endsWith(",injected"), + isInternalScript: script.is_debugger_script }; } @@ -276,6 +273,11 @@ DebuggerScript.stepOutOfFunction = function(execState, callFrame) execState.prepareStep(Debug.StepAction.StepOut, 1); } +DebuggerScript.clearStepping = function() +{ + Debug.clearStepping(); +} + // Returns array in form: // [ 0, ] in case of success // or [ 1, , , , ] in case of compile error, numbers are 1-based. @@ -296,7 +298,7 @@ DebuggerScript.liveEditScriptSource = function(scriptId, newSource, preview) var changeLog = []; try { var result = Debug.LiveEdit.SetScriptSource(scriptToEdit, newSource, preview, changeLog); - return [0, result]; + return [0, result.stack_modified]; } catch (e) { if (e instanceof Debug.LiveEdit.Failure && "details" in e) { var details = e.details; @@ -357,6 +359,17 @@ DebuggerScript.isEvalCompilation = function(eventData) return (script.compilationType() === Debug.ScriptCompilationType.Eval); } +DebuggerScript._displayFunctionName = function(funcMirror) +{ + if (!funcMirror.resolved()) + return undefined + var displayName; + var valueMirror = funcMirror.property("displayName").value(); + if (valueMirror && valueMirror.isString()) + displayName = valueMirror.value(); + return displayName || funcMirror.name() || funcMirror.inferredName(); +} + // NOTE: This function is performance critical, as it can be run on every // statement that generates an async event (like addEventListener) to support // asynchronous call stacks. Thus, when possible, initialize the data lazily. @@ -458,14 +471,19 @@ DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror, callerFrame, sc function functionName() { - var func = ensureFuncMirror(); - if (!func.resolved()) - return undefined; - var displayName; - var valueMirror = func.property("displayName").value(); - if (valueMirror && valueMirror.isString()) - displayName = valueMirror.value(); - return displayName || func.name() || func.inferredName(); + return DebuggerScript._displayFunctionName(ensureFuncMirror()); + } + + function functionLine() + { + var location = ensureFuncMirror().sourceLocation(); + return location ? location.line : 0; + } + + function functionColumn() + { + var location = ensureFuncMirror().sourceLocation(); + return location ? location.column : 0; } function evaluate(expression, scopeExtension) @@ -475,7 +493,7 @@ DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror, callerFrame, sc function restart() { - return Debug.LiveEdit.RestartFrame(frameMirror); + return frameMirror.restart(); } function setVariableValue(scopeNumber, variableName, newValue) @@ -511,6 +529,8 @@ DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror, callerFrame, sc "column": column, "scriptName": scriptName, "functionName": functionName, + "functionLine": functionLine, + "functionColumn": functionColumn, "thisObject": thisObject, "scopeChain": lazyScopeChain, "scopeType": lazyScopeTypes, From 22211953a76f2676f5e43b296118f851235ee504 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Wed, 6 Jan 2016 02:39:10 +0300 Subject: [PATCH 02/23] Add 'shareSecurityToken' method --- src/debug.cc | 36 ++++++++++++++++++++++++++++++++++-- v8-debug.js | 12 ++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/debug.cc b/src/debug.cc index 43482e6..3b4e0f2 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -58,15 +58,18 @@ namespace nodex { if (expression.IsEmpty()) RETURN(Undefined()); + Local current_context = Isolate::GetCurrent()->GetCurrentContext(); Local debug_context = v8::Debug::GetDebugContext(); #if (NODE_MODULE_VERSION > 45) if (debug_context.IsEmpty()) { // Force-load the debug context. - v8::Debug::GetMirror(info.GetIsolate()->GetCurrentContext(), info[0]); + v8::Debug::GetMirror(current_context, New()); debug_context = v8::Debug::GetDebugContext(); } -#endif + // Share security token + debug_context->SetSecurityToken(current_context->GetSecurityToken()); +#endif Context::Scope context_scope(debug_context); TryCatch tryCatch; @@ -77,12 +80,39 @@ namespace nodex { }; static NAN_METHOD(AllowNatives) { + // TODO: deprecate this useless method const char allow_natives_syntax[] = "--allow_natives_syntax"; v8::V8::SetFlagsFromString(allow_natives_syntax, sizeof(allow_natives_syntax) - 1); RETURN(Undefined()); } + static NAN_METHOD(ShareSecurityToken) { + Local current_context = info.GetIsolate()->GetCurrentContext(); + Local debug_context = v8::Debug::GetDebugContext(); +#if (NODE_MODULE_VERSION > 45) + if (debug_context.IsEmpty()) { + // Force-load the debug context. + v8::Debug::GetMirror(current_context, New()); + debug_context = v8::Debug::GetDebugContext(); + } +#endif + // Share security token + debug_context->SetSecurityToken(current_context->GetSecurityToken()); + } + + static NAN_METHOD(UnshareSecurityToken) { + Local current_context = info.GetIsolate()->GetCurrentContext(); + Local debug_context = v8::Debug::GetDebugContext(); +#if (NODE_MODULE_VERSION > 45) + if (debug_context.IsEmpty()) { + // Force-load the debug context. + v8::Debug::GetMirror(current_context, New()); + debug_context = v8::Debug::GetDebugContext(); + } +#endif + debug_context->UseDefaultSecurityToken(); + } private: Debug() {} ~Debug() {} @@ -97,6 +127,8 @@ namespace nodex { SetMethod(target, "sendCommand", Debug::SendCommand); SetMethod(target, "runScript", Debug::RunScript); SetMethod(target, "allowNatives", Debug::AllowNatives); + SetMethod(target, "shareSecurityToken", Debug::ShareSecurityToken); + SetMethod(target, "unshareSecurityToken", Debug::UnshareSecurityToken); } NODE_MODULE(debug, Initialize) diff --git a/v8-debug.js b/v8-debug.js index 0d94df8..572758c 100644 --- a/v8-debug.js +++ b/v8-debug.js @@ -107,10 +107,14 @@ function V8Debug() { // After node 0.12.0 this function serves to allocate Debug Context // like a persistent value, that saves all our changes. this._setDebugEventListener(); + // We need to share security token between current and debug context to + // get access to evaluation functions + this._shareSecurityToken(); this._wrapDebugCommandProcessor(); this.once('close', function() { this._unwrapDebugCommandProcessor(); + this._unshareSecurityToken(); this._unsetDebugEventListener(); process.nextTick(function() { this.removeAllListeners(); @@ -130,6 +134,14 @@ V8Debug.prototype._unsetDebugEventListener = function() { Debug.setListener(null); }; +V8Debug.prototype._shareSecurityToken = function() { + binding.shareSecurityToken(); +}; + +V8Debug.prototype._unshareSecurityToken = function() { + binding.unshareSecurityToken(); +}; + V8Debug.prototype._wrapDebugCommandProcessor = function() { var proto = this.get('DebugCommandProcessor.prototype'); overrides.processDebugRequest_ = proto.processDebugRequest; From 6899e0b17c086da9a1da83f62adb3e48b72a3eb1 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Wed, 6 Jan 2016 02:42:55 +0300 Subject: [PATCH 03/23] Fix InjectedScriptHost for NODE_NEXT --- InjectedScript/InjectedScriptHost.js | 58 +++++++++-- src/InjectedScriptHost.cc | 72 +++++++------ src/InjectedScriptHost.h | 17 +--- test/injected-script-source.js | 146 +++++++++++++++++++++++++++ 4 files changed, 240 insertions(+), 53 deletions(-) create mode 100644 test/injected-script-source.js diff --git a/InjectedScript/InjectedScriptHost.js b/InjectedScript/InjectedScriptHost.js index 38d2810..f7e6ebe 100644 --- a/InjectedScript/InjectedScriptHost.js +++ b/InjectedScript/InjectedScriptHost.js @@ -1,15 +1,55 @@ "use strict"; (function (binding, DebuggerScript) { - function InjectedScriptHost() {} - + var lastBoundObjectId = 0; + var idToWrappedObject = new Map(); + var idToObjectGroupName = new Map(); + var nameToObjectGroup = new Map(); + + function InjectedScriptHost() { + } + InjectedScriptHost.prototype = binding.InjectedScriptHost; + InjectedScriptHost.prototype.bind = function(value, groupName) { + if (lastBoundObjectId <= 0) + lastBoundObjectId = 1; + + var id = lastBoundObjectId++; + idToWrappedObject.set(id, value); + + if (!groupName) return; + if (id < 0) return; + + idToObjectGroupName.set(id, groupName); + + if (!nameToObjectGroup.has(groupName)) + nameToObjectGroup.set(groupName, [id]); + else + nameToObjectGroup.get(groupName).push(id); + + return id; + }; + + InjectedScriptHost.prototype.objectForId = function(id) { + if (!Number(id)) return; + return idToWrappedObject.get(id); + }; + + InjectedScriptHost.prototype.idToObjectGroupName = function(id) { + if (!Number(id)) return; + return idToObjectGroupName.get(id) || ''; + } + InjectedScriptHost.prototype.isHTMLAllCollection = function(object) { //We don't have `all` collection in NodeJS return false; }; + InjectedScriptHost.prototype.isDOMWrapper = function(object) { + return false; + }; + InjectedScriptHost.prototype.suppressWarningsAndCallFunction = function(func, receiver, args) { return this.callFunction(func, receiver, args); }; @@ -17,17 +57,21 @@ InjectedScriptHost.prototype.functionDetails = function(fun) { var details = this.functionDetailsWithoutScopes(fun); var scopes = DebuggerScript.getFunctionScopes(fun); - + if (scopes && scopes.length) { details.rawScopes = scopes; } - + return details; }; - InjectedScriptHost.prototype.getInternalProperties = function(value) { - return DebuggerScript.getInternalProperties(value); + InjectedScriptHost.prototype.generatorObjectDetails = function(object) { + return DebuggerScript.getGeneratorObjectDetails(object); }; - + + InjectedScriptHost.prototype.collectionEntries = function(object) { + return DebuggerScript.getCollectionEntries(object); + }; + return new InjectedScriptHost(); }); diff --git a/src/InjectedScriptHost.cc b/src/InjectedScriptHost.cc index 82839ef..aa2ce3c 100644 --- a/src/InjectedScriptHost.cc +++ b/src/InjectedScriptHost.cc @@ -4,7 +4,6 @@ #include "tools.h" using v8::Isolate; -using v8::Handle; using v8::Local; using v8::Value; using v8::Boolean; @@ -31,7 +30,7 @@ using Nan::EmptyString; using Nan::Utf8String; namespace nodex { - void InjectedScriptHost::Initialize(Handle target) { + void InjectedScriptHost::Initialize(Local target) { Local injectedScriptHost = New(); SetMethod(injectedScriptHost, "eval", Eval); SetMethod(injectedScriptHost, "evaluateWithExceptionDetails", EvaluateWithExceptionDetails); @@ -40,11 +39,12 @@ namespace nodex { SetMethod(injectedScriptHost, "internalConstructorName", InternalConstructorName); SetMethod(injectedScriptHost, "functionDetailsWithoutScopes", FunctionDetailsWithoutScopes); SetMethod(injectedScriptHost, "callFunction", CallFunction); + SetMethod(injectedScriptHost, "getInternalProperties", GetInternalProperties); SET(target, "InjectedScriptHost", injectedScriptHost); } - Handle InjectedScriptHost::createExceptionDetails(Handle message) { + Local InjectedScriptHost::createExceptionDetails(Local message) { EscapableHandleScope scope; Local exceptionDetails = New(); @@ -133,51 +133,48 @@ namespace nodex { RETURN(Undefined()); }; - Local InjectedScriptHost::functionDisplayName(Handle function) { - EscapableHandleScope scope; + const char* InjectedScriptHost::toCoreStringWithUndefinedOrNullCheck(Local result) { + if (result.IsEmpty() || result->IsNull() || result->IsUndefined()) + return ""; + else + return *Utf8String(result); + }; - Local value = CHK(To(function->GetDisplayName())); - if (value->Length()) - return scope.Escape(value); + Local InjectedScriptHost::functionDisplayName(Local function) { + Local value = function->GetDisplayName(); + if (value->IsString() && Local::Cast(value)->Length()) + return Local::Cast(value); - value = CHK(To(function->GetName())); - if (value->Length()) - return scope.Escape(value); + value = function->GetName(); + if (value->IsString() && v8::Local::Cast(value)->Length()) + return v8::Local::Cast(value); - value = CHK(To(function->GetInferredName())); - if (value->Length()) - return scope.Escape(value); + value = function->GetInferredName(); + if (value->IsString() && v8::Local::Cast(value)->Length()) + return v8::Local::Cast(value); - return scope.Escape(EmptyString()); + return v8::Local(); }; NAN_METHOD(InjectedScriptHost::InternalConstructorName) { - if (info.Length() < 1) - return ThrowError("One argument expected."); - if (!info[0]->IsObject()) - return ThrowTypeError("The argument must be an object."); + if (info.Length() < 1 || !info[0]->IsObject()) + return; Local object = CHK(To(info[0])); Local result = object->GetConstructorName(); - const char* result_type; - if (result.IsEmpty() || result->IsNull() || result->IsUndefined()) - result_type = ""; - else - result_type = *Utf8String(info[0]); - - if (!result.IsEmpty() && strcmp(result_type, "Object") == 0) { + if (!result.IsEmpty() && strcmp(toCoreStringWithUndefinedOrNullCheck(result), "Object") == 0) { Local constructorSymbol = CHK(New("constructor")); if (object->HasRealNamedProperty(constructorSymbol) && !object->HasRealNamedCallbackProperty(constructorSymbol)) { TryCatch tryCatch; Local constructor = object->GetRealNamedProperty(constructorSymbol); if (!constructor.IsEmpty() && constructor->IsFunction()) { - Local constructorName = functionDisplayName(Handle::Cast(constructor)); + Local constructorName = functionDisplayName(Local::Cast(constructor)); if (!constructorName.IsEmpty() && !tryCatch.HasCaught()) result = constructorName; } } - if (strcmp(result_type, "Object") == 0 && object->IsFunction()) + if (strcmp(toCoreStringWithUndefinedOrNullCheck(result), "Object") == 0 && object->IsFunction()) result = CHK(New("Function")); } @@ -203,7 +200,7 @@ namespace nodex { Local result = New(); SET(result, "location", location); - Handle name = functionDisplayName(function); + Local name = functionDisplayName(function); SET(result, "functionName", name.IsEmpty() ? EmptyString() : name); SET(result, "isGenerator", New(function->IsGeneratorFunction())); @@ -217,8 +214,8 @@ namespace nodex { if (!info[0]->IsFunction()) return ThrowTypeError("Argument 0 must be a function."); - Handle function = Handle::Cast(info[0]); - Handle receiver = info[1]; + Local function = Local::Cast(info[0]); + Local receiver = info[1]; TryCatch tryCatch; MaybeLocal result; @@ -232,9 +229,9 @@ namespace nodex { if (!info[2]->IsArray()) return ThrowTypeError("Argument 2 must be an array."); - Handle arguments = Handle::Cast(info[2]); + Local arguments = Local::Cast(info[2]); int argc = arguments->Length(); - Handle *argv = new Handle[argc]; + Local *argv = new Local[argc]; for (int i = 0; i < argc; ++i) argv[i] = CHK(Get(arguments, i)); @@ -260,4 +257,13 @@ namespace nodex { RETURN(CHK(result)); }; + + NAN_METHOD(InjectedScriptHost::GetInternalProperties) { + if (info.Length() < 1 || !info[0]->IsObject()) + return; + + MaybeLocal result = v8::Debug::GetInternalProperties(info.GetIsolate(), info[0]); + + RETURN(CHK(result)); + } } diff --git a/src/InjectedScriptHost.h b/src/InjectedScriptHost.h index ccb1bd0..144dbd8 100644 --- a/src/InjectedScriptHost.h +++ b/src/InjectedScriptHost.h @@ -15,20 +15,11 @@ namespace nodex { static NAN_METHOD(InternalConstructorName); static NAN_METHOD(FunctionDetailsWithoutScopes); static NAN_METHOD(CallFunction); - /* - static v8::Local New(const v8::CpuProfile* node); - static Nan::Persistent profiles; - */ + static NAN_METHOD(GetInternalProperties); private: - static v8::Handle createExceptionDetails(v8::Handle message); - static v8::Local functionDisplayName(v8::Handle function); - - /* - static NAN_METHOD(Delete); - static void Initialize(); - static Nan::Persistent profile_template_; - static uint32_t uid_counter; - */ + static v8::Local createExceptionDetails(v8::Local message); + static v8::Local functionDisplayName(v8::Local function); + static const char* toCoreStringWithUndefinedOrNullCheck(v8::Local result); }; } //namespace nodex diff --git a/test/injected-script-source.js b/test/injected-script-source.js new file mode 100644 index 0000000..23f8b88 --- /dev/null +++ b/test/injected-script-source.js @@ -0,0 +1,146 @@ +var expect = require('chai').expect; +var debug = require('../'); +var NODE_NEXT = require('../tools/NODE_NEXT.js'); + +if (!NODE_NEXT) return; + +describe.only('InjectedScriptSource', function() { + before(function() { + debug.enableWebkitProtocol(); + }); + + it('isPrimitiveValue', function(done) { + debug.registerAgentCommand('isPrimitiveValue', function(args, response, IScript, DScript) { + expect(IScript.isPrimitiveValue(42)).to.be.equal(true); + expect(IScript.isPrimitiveValue({})).to.be.equal(undefined); + done(); + }); + + debug.sendCommand('isPrimitiveValue'); + }); + + it('wrapObject', function(done) { + debug.registerAgentCommand('wrapObject', function(args, response, IScript, DScript) { + expect(IScript.wrapObject.bind(IScript, {a:1}, 'console', true, false)).to.not.throw(); + expect(IScript.wrapObject.bind(IScript, {a:1}, 'console', true, true)).to.not.throw(); + expect(IScript.wrapObject.bind(IScript, 'string', 'console', true, true)).to.not.throw(); + expect(IScript.wrapObject.bind(IScript, undefined, 'console', true, true)).to.not.throw(); + expect(IScript.wrapObject.bind(IScript, {a:1}, 'console', false, false)).to.not.throw(); + expect(IScript.wrapObject.bind(IScript, {a:1}, 'console', false, true)).to.not.throw(); + done(); + }); + + debug.sendCommand('wrapObject'); + }); + + it('wrapTable', function(done) { + debug.registerAgentCommand('wrapTable', function(args, response, IScript, DScript) { + expect(IScript.wrapTable.bind(IScript, true, {a:1}, ['a'])).to.not.throw(); + done(); + }); + + debug.sendCommand('wrapTable'); + }); + + it('getProperties', function(done) { + debug.registerAgentCommand('getProperties', function(args, response, IScript, DScript) { + var objectId = IScript.wrapObject({a:1, b: Symbol('b'), get c() {}}, 'console', true, false).objectId; + IScript.getProperties(objectId, true, true, false); + IScript.getProperties(objectId, true, false, false); + IScript.getProperties(objectId, false, false, false); + IScript.getProperties(objectId, false, true, false); + IScript.getProperties(objectId, true, false, true); + + done(); + }); + + debug.sendCommand('getProperties'); + }); + + it('getInternalProperties', function(done) { + debug.registerAgentCommand('getInternalProperties', function(args, response, IScript, DScript) { + var objectId = IScript.wrapObject({a:1, b: Symbol('b'), get c() {}}, 'console', true, false).objectId; + IScript.getInternalProperties(objectId); + + done(); + }); + + debug.sendCommand('getInternalProperties'); + }); + + it('getFunctionDetails', function(done) { + debug.registerAgentCommand('getFunctionDetails', function(args, response, IScript, DScript) { + var c = 1; + function d(a, b) { c = a; } + function * g(a, b) { yield c; return c = a; } + + var functionId = IScript.wrapObject(d, 'console', true, false).objectId; + var generatorId = IScript.wrapObject(g, 'console', true, false).objectId; + IScript.getFunctionDetails(functionId); + IScript.getFunctionDetails(generatorId); + + done(); + }); + + debug.sendCommand('getFunctionDetails'); + }); + + it('getGeneratorObjectDetails', function(done) { + debug.registerAgentCommand('getGeneratorObjectDetails', function(args, response, IScript, DScript) { + var c = 1; + function * g(a, b) { yield c; return c = b; } + var gen = g(1, 2); + + var genId = IScript.wrapObject(gen, 'console', true, false).objectId; + IScript.getGeneratorObjectDetails(genId); + + done(); + }); + + debug.sendCommand('getGeneratorObjectDetails'); + }); + + it('getCollectionEntries', function(done) { + debug.registerAgentCommand('getCollectionEntries', function(args, response, IScript, DScript) { + var map = new Map().set('1', 1); + + var mapId = IScript.wrapObject(map, 'console', true, false).objectId; + IScript.getCollectionEntries(mapId); + + done(); + }); + + debug.sendCommand('getCollectionEntries'); + }); + + it('evaluate', function(done) { + debug.registerAgentCommand('evaluate', function(args, response, IScript, DScript) { + IScript.evaluate('{a: 1}', 'console'); + IScript.evaluate('{a: 1}', 'console', false, true); + IScript.evaluate('{a: 1}', 'console', false, false, true); + IScript.evaluate('{a: 1}', 'console', true); + + done(); + }); + + debug.sendCommand('evaluate'); + }); + + it('callFunctionOn', function(done) { + debug.registerAgentCommand('callFunctionOn', function(args, response, IScript, DScript) { + var contextId = IScript.wrapObject({a: 1, b: 2}, 'console', true, false).objectId; + var args = [ + {value: 3}, + {objectId: IScript.wrapObject({b: 4}, 'console', true, false).objectId} + ]; + var expression = 'function(a, b) { return this.a + this.b + a + b.b; }'; + + var result = IScript.callFunctionOn(contextId, expression, JSON.stringify(args)).result; + expect(result.value).to.be.equal(10); + + done(); + }); + + debug.sendCommand('callFunctionOn'); + }); +}); From d6eda446e06f8eca62d320a81f4c7ba9245979a3 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Wed, 6 Jan 2016 02:44:09 +0300 Subject: [PATCH 04/23] Fix 'runInDebugContext' method --- v8-debug.js | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/v8-debug.js b/v8-debug.js index 572758c..080d022 100644 --- a/v8-debug.js +++ b/v8-debug.js @@ -163,12 +163,14 @@ V8Debug.prototype._unwrapDebugCommandProcessor = function() { V8Debug.prototype.register = V8Debug.prototype.registerCommand = function(name, func) { - overrides.extendedProcessDebugJSONRequestHandles_[name] = func; + var proto = this.get('DebugCommandProcessor.prototype'); + proto.extendedProcessDebugJSONRequestHandles_[name] = func; }; V8Debug.prototype.registerAsync = V8Debug.prototype.registerAsyncCommand = function(name, func) { - overrides.extendedProcessDebugJSONRequestAsyncHandles_[name] = func; + var proto = this.get('DebugCommandProcessor.prototype'); + proto.extendedProcessDebugJSONRequestAsyncHandles_[name] = func; }; V8Debug.prototype.command = @@ -193,14 +195,13 @@ V8Debug.prototype.commandToEvent = function(request, response) { }; V8Debug.prototype.registerEvent = function(name) { - overrides.extendedProcessDebugJSONRequestHandles_[name] = this.commandToEvent; + var proto = this.get('DebugCommandProcessor.prototype'); + proto.extendedProcessDebugJSONRequestHandles_[name] = this.commandToEvent; }; V8Debug.prototype.get = V8Debug.prototype.runInDebugContext = function(script) { - if (typeof script == 'function') script = script.toString() + '()'; - - script = /\);$/.test(script) ? script : '(' + script + ');'; + if (typeof script == 'function') script = '(' + script.toString() + ')()'; return binding.runScript(script); }; @@ -242,25 +243,25 @@ V8Debug.prototype.enableWebkitProtocol = function() { InjectedScriptHostSource, InjectedScriptHost; - function prepareSource(source) { - return 'var ToggleMirrorCache = ToggleMirrorCache || function() {};\n' + - '(function() {' + - ('' + source).replace(/^.*?"use strict";(\r?\n.*?)*\(/m, '\r\n"use strict";\nreturn (') + - '}());'; - } + this.runInDebugContext('ToggleMirrorCache = ToggleMirrorCache || function() {}'); - DebuggerScriptSource = prepareSource(fs.readFileSync(DebuggerScriptLink, 'utf8')); + DebuggerScriptSource = fs.readFileSync(DebuggerScriptLink, 'utf8'); DebuggerScript = this.runInDebugContext(DebuggerScriptSource); - InjectedScriptSource = prepareSource(fs.readFileSync(InjectedScriptLink, 'utf8')); + InjectedScriptSource = fs.readFileSync(InjectedScriptLink, 'utf8'); InjectedScript = this.runInDebugContext(InjectedScriptSource); - InjectedScriptHostSource = prepareSource(fs.readFileSync(InjectedScriptHostLink, 'utf8')); + InjectedScriptHostSource = fs.readFileSync(InjectedScriptHostLink, 'utf8'); InjectedScriptHost = this.runInDebugContext(InjectedScriptHostSource)(binding, DebuggerScript); var injectedScript = InjectedScript(InjectedScriptHost, global, 1); this.registerAgentCommand = function(command, parameters, callback) { + if (typeof parameters === 'function') { + callback = parameters; + parameters = []; + } + this.registerCommand(command, new WebkitProtocolCallback(parameters, callback)); }; From fd3dff570e79d9284e6b515afe5631ccbf892834 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Wed, 6 Jan 2016 20:11:52 +0300 Subject: [PATCH 05/23] Implement 'JavaScriptCallFrame' --- InjectedScript/JavaScriptCallFrame.js | 110 ++++++++++++++++++++++++++ binding.gyp | 3 +- src/JavaScriptCallFrame.cc | 98 +++++++++++++++++++++++ src/JavaScriptCallFrame.h | 18 +++++ src/debug.cc | 2 + src/tools.h | 6 +- v8-debug.js | 14 ++++ 7 files changed, 247 insertions(+), 4 deletions(-) create mode 100644 InjectedScript/JavaScriptCallFrame.js create mode 100644 src/JavaScriptCallFrame.cc create mode 100644 src/JavaScriptCallFrame.h diff --git a/InjectedScript/JavaScriptCallFrame.js b/InjectedScript/JavaScriptCallFrame.js new file mode 100644 index 0000000..9a48122 --- /dev/null +++ b/InjectedScript/JavaScriptCallFrame.js @@ -0,0 +1,110 @@ +"use strict"; + +(function(binding) { + function JavaScriptCallFrame(proto) { + Object.defineProperty(this, 'proto', { + get: function() { return proto; } + }); + } + + JavaScriptCallFrame.prototype = binding.JavaScriptCallFrame; + + Object.defineProperties(JavaScriptCallFrame.prototype, { + setVariableValue: { + value: function(scopeNumber, variableName, resolvedValue) { + return this.proto.setVariableValue(scopeNumber, variableName, resolvedValue); + } + }, + + caller: { + get: function() { + var caller = this.proto.caller; + if (!caller) return; + + return new JavaScriptCallFrame(caller); + } + }, + + sourceID: { + get: function() { + return Number(this.proto.sourceID()); + } + }, + + line: { + get: function() { + return Number(this.proto.line()); + } + }, + + column: { + get: function() { + return Number(this.proto.column()); + } + }, + + scriptName: { + get: function() { + return String(this.proto.scriptName()); + } + }, + + functionName: { + get: function() { + return String(this.proto.functionName()); + } + }, + + functionLine: { + get: function() { + return Number(this.proto.functionLine()); + } + }, + + functionColumn: { + get: function() { + return Number(this.proto.functionColumn()); + } + }, + + scopeChain: { + get: function() { + var scopeChain = this.proto.scopeChain(); + return scopeChain.slice(); + } + }, + + scopeType: { + value: function(index) { + var scopeType = this.proto.scopeType(); + return Number(scopeType[index]); + } + }, + + thisObject: { + get: function() { + return this.proto.thisObject; + } + }, + + stepInPositions: { + get: function() { + return String(this.proto.stepInPositions()); + } + }, + + isAtReturn: { + get: function() { + return Boolean(this.proto.isAtReturn); + } + }, + + returnValue: { + get: function() { + return this.proto.returnValue; + } + } + }); + + return JavaScriptCallFrame; +}); diff --git a/binding.gyp b/binding.gyp index 14a0c20..b775ad7 100644 --- a/binding.gyp +++ b/binding.gyp @@ -15,7 +15,8 @@ 'conditions' : [ ['node_next=="true"', { 'sources': [ - 'src/InjectedScriptHost.cc' + 'src/InjectedScriptHost.cc', + 'src/JavaScriptCallFrame.cc' ], 'defines': ['NODE_NEXT=1'] }] diff --git a/src/JavaScriptCallFrame.cc b/src/JavaScriptCallFrame.cc new file mode 100644 index 0000000..f73fc1a --- /dev/null +++ b/src/JavaScriptCallFrame.cc @@ -0,0 +1,98 @@ +#include + +#include "JavaScriptCallFrame.h" +#include "tools.h" + +using v8::Isolate; +using v8::Local; +using v8::Value; +using v8::Number; +using v8::Integer; +using v8::String; +using v8::Object; +using v8::Message; +using v8::Function; +using Nan::To; +using Nan::New; +using Nan::Get; +using Nan::Set; +using Nan::SetMethod; +using Nan::EscapableHandleScope; +using Nan::Undefined; +using Nan::TryCatch; +using Nan::ThrowError; +using Nan::ThrowTypeError; +using Nan::MaybeLocal; + +namespace nodex { + void JavaScriptCallFrame::Initialize(Local target) { + Local javaScriptCallFrame = New(); + SetMethod(javaScriptCallFrame, "evaluateWithExceptionDetails", EvaluateWithExceptionDetails); + SetMethod(javaScriptCallFrame, "restart", Restart); + + SET(target, "JavaScriptCallFrame", javaScriptCallFrame); + } + + Local JavaScriptCallFrame::createExceptionDetails(Local message) { + EscapableHandleScope scope; + + Local exceptionDetails = New(); + SET(exceptionDetails, "text", message->Get()); + + SET(exceptionDetails, "url", message->GetScriptOrigin().ResourceName()); + SET(exceptionDetails, "scriptId", New((int32_t)message->GetScriptOrigin().ScriptID()->Value())); + SET(exceptionDetails, "line", New(message->GetLineNumber())); + SET(exceptionDetails, "column", New(message->GetStartColumn())); + + if (!message->GetStackTrace().IsEmpty()) + SET(exceptionDetails, "stackTrace", message->GetStackTrace()->AsArray()); + else + SET(exceptionDetails, "stackTrace", Undefined()); + + return scope.Escape(exceptionDetails); + }; + + NAN_METHOD(JavaScriptCallFrame::EvaluateWithExceptionDetails) { + Local callFrame = CHK(To(CHK(Get(info.Holder(), CHK(New("proto")))))); + Local evalFunction = Local::Cast(CHK(Get(callFrame, CHK(New("evaluate"))))); + + Local expression = info[0]; + Local scopeExtension = info[1]; + v8::Local argv[] = { + expression, + scopeExtension + }; + + Local wrappedResult = New(); + + TryCatch tryCatch; + MaybeLocal result; + + result = evalFunction->Call(callFrame, 2, argv); + + if (tryCatch.HasCaught()) { + SET(wrappedResult, "result", tryCatch.Exception()); + SET(wrappedResult, "exceptionDetails", createExceptionDetails(tryCatch.Message())); + } else { + SET(wrappedResult, "result", CHK(result)); + SET(wrappedResult, "exceptionDetails", Undefined()); + } + + RETURN(wrappedResult); + }; + + NAN_METHOD(JavaScriptCallFrame::Restart) { + Local callFrame = CHK(To(CHK(Get(info.Holder(), CHK(New("proto")))))); + Local restartFunction = Local::Cast(CHK(Get(callFrame, CHK(New("restart"))))); + + TryCatch tryCatch; + MaybeLocal result; + + v8::Debug::SetLiveEditEnabled(info.GetIsolate(), true); + result = restartFunction->Call(callFrame, 0, NULL); + v8::Debug::SetLiveEditEnabled(info.GetIsolate(), false); + + MAYBE_RETHROW(); + RETURN(CHK(result)); + }; +} diff --git a/src/JavaScriptCallFrame.h b/src/JavaScriptCallFrame.h new file mode 100644 index 0000000..f8e7c86 --- /dev/null +++ b/src/JavaScriptCallFrame.h @@ -0,0 +1,18 @@ +#ifndef X_JAVASCRIPT_CALL_FRAME_ +#define X_JAVASCRIPT_CALL_FRAME_ + +#include + +namespace nodex { + + class JavaScriptCallFrame { + public: + static void Initialize(v8::Local target); + static NAN_METHOD(EvaluateWithExceptionDetails); + static NAN_METHOD(Restart); + private: + static v8::Local createExceptionDetails(v8::Local message); + }; + +} //namespace nodex +#endif // X_JAVASCRIPT_CALL_FRAME_ diff --git a/src/debug.cc b/src/debug.cc index 3b4e0f2..4149c09 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -4,6 +4,7 @@ #include "tools.h" #if (NODE_NEXT) #include "InjectedScriptHost.h" +#include "JavaScriptCallFrame.h" #endif using v8::Isolate; @@ -121,6 +122,7 @@ namespace nodex { NAN_MODULE_INIT(Initialize) { #if (NODE_NEXT) InjectedScriptHost::Initialize(target); + JavaScriptCallFrame::Initialize(target); #endif SetMethod(target, "call", Debug::Call); diff --git a/src/tools.h b/src/tools.h index ea65e27..3b70899 100644 --- a/src/tools.h +++ b/src/tools.h @@ -14,14 +14,14 @@ #define RUNSCRIPT(EXPRESSION, RESULT) while (true) { \ Nan::MaybeLocal script = Nan::CompileScript(EXPRESSION);\ - if (tryCatch.HasCaught()) break; \ + if (tryCatch.HasCaught()) break; \ RESULT = Nan::RunScript(CHK(script)); \ break; \ } #define MAYBE_RETHROW() \ - if (tryCatch.HasCaught()) { \ - tryCatch.ReThrow(); \ + if (tryCatch.HasCaught()) { \ + tryCatch.ReThrow(); \ return; \ } diff --git a/v8-debug.js b/v8-debug.js index 080d022..e0b31a1 100644 --- a/v8-debug.js +++ b/v8-debug.js @@ -18,6 +18,7 @@ function InjectedScriptDir(link) { var DebuggerScriptLink = InjectedScriptDir('DebuggerScript.js'); var InjectedScriptLink = InjectedScriptDir('InjectedScriptSource.js'); var InjectedScriptHostLink = InjectedScriptDir('InjectedScriptHost.js'); +var JavaScriptCallFrameLink = InjectedScriptDir('JavaScriptCallFrame.js'); var overrides = { extendedProcessDebugJSONRequestHandles_: {}, @@ -254,6 +255,9 @@ V8Debug.prototype.enableWebkitProtocol = function() { InjectedScriptHostSource = fs.readFileSync(InjectedScriptHostLink, 'utf8'); InjectedScriptHost = this.runInDebugContext(InjectedScriptHostSource)(binding, DebuggerScript); + JavaScriptCallFrameSource = fs.readFileSync(JavaScriptCallFrameLink, 'utf8'); + JavaScriptCallFrame = this.runInDebugContext(JavaScriptCallFrameSource)(binding, DebuggerScript); + var injectedScript = InjectedScript(InjectedScriptHost, global, 1); this.registerAgentCommand = function(command, parameters, callback) { @@ -265,6 +269,15 @@ V8Debug.prototype.enableWebkitProtocol = function() { this.registerCommand(command, new WebkitProtocolCallback(parameters, callback)); }; + this.wrapCallFrames = function(execState, maximumLimit, scopeDetails) { + var scopeBits = 2; + + if (maximumLimit < 0) throw new Error('Incorrect stack trace limit.'); + var data = (maximumLimit << scopeBits) | scopeDetails; + + return JavaScriptCallFrame.currentCallFrame(execState, data);; + }; + this._webkitProtocolEnabled = true; function WebkitProtocolCallback(argsList, callback) { @@ -282,6 +295,7 @@ V8Debug.prototype.enableWebkitProtocol = function() { } }; +V8Debug.prototype.wrapCallFrames = V8Debug.prototype.registerAgentCommand = function(command, parameters, callback) { throw new Error('Use "enableWebkitProtocol" before using this method'); }; From dc38f62eadcd15638b754793abef2c0e7a059577 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Thu, 7 Jan 2016 20:19:54 +0300 Subject: [PATCH 06/23] Add 'releaseObject' API --- InjectedScript/InjectedScriptHost.js | 29 +++++++++++++++++++++++++++- v8-debug.js | 18 +++++++++++++---- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/InjectedScript/InjectedScriptHost.js b/InjectedScript/InjectedScriptHost.js index f7e6ebe..793b2d0 100644 --- a/InjectedScript/InjectedScriptHost.js +++ b/InjectedScript/InjectedScriptHost.js @@ -18,8 +18,8 @@ var id = lastBoundObjectId++; idToWrappedObject.set(id, value); - if (!groupName) return; if (id < 0) return; + if (groupName == null) return id; idToObjectGroupName.set(id, groupName); @@ -31,6 +31,33 @@ return id; }; + InjectedScriptHost.prototype.unbind = function(id) { + idToWrappedObject.delete(id); + idToObjectGroupName.delete(id); + }; + + InjectedScriptHost.prototype.releaseObject = function(objectId) { + var parsedObjectId; + try { + parsedObjectId = JSON.parse(objectId); + } catch (e) { return; } + + this.unbind(parsedObjectId.id); + }; + + InjectedScriptHost.prototype.releaseObjectGroup = function(groupName) { + if (!groupName) return; + + var group = nameToObjectGroup.get(groupName); + if (!group) return; + + group.forEach(function(id) { + this.unbind(id); + }, this); + + nameToObjectGroup.delete(groupName); + }; + InjectedScriptHost.prototype.objectForId = function(id) { if (!Number(id)) return; return idToWrappedObject.get(id); diff --git a/v8-debug.js b/v8-debug.js index e0b31a1..94cde05 100644 --- a/v8-debug.js +++ b/v8-debug.js @@ -182,7 +182,7 @@ function sendCommand(name, attributes, userdata) { seq: 0, type: 'request', command: name, - arguments: attributes || {} + arguments: attributes }; binding.sendCommand(JSON.stringify(message)); }; @@ -256,9 +256,9 @@ V8Debug.prototype.enableWebkitProtocol = function() { InjectedScriptHost = this.runInDebugContext(InjectedScriptHostSource)(binding, DebuggerScript); JavaScriptCallFrameSource = fs.readFileSync(JavaScriptCallFrameLink, 'utf8'); - JavaScriptCallFrame = this.runInDebugContext(JavaScriptCallFrameSource)(binding, DebuggerScript); + JavaScriptCallFrame = this.runInDebugContext(JavaScriptCallFrameSource)(binding); - var injectedScript = InjectedScript(InjectedScriptHost, global, 1); + var injectedScript = InjectedScript(InjectedScriptHost, global, process.pid); this.registerAgentCommand = function(command, parameters, callback) { if (typeof parameters === 'function') { @@ -274,8 +274,16 @@ V8Debug.prototype.enableWebkitProtocol = function() { if (maximumLimit < 0) throw new Error('Incorrect stack trace limit.'); var data = (maximumLimit << scopeBits) | scopeDetails; + var currentCallFrame = DebuggerScript.currentCallFrame(execState, data); + return new JavaScriptCallFrame(currentCallFrame); + }; + + this.releaseObject = function(name) { + return InjectedScriptHost.releaseObject(name); + }; - return JavaScriptCallFrame.currentCallFrame(execState, data);; + this.releaseObjectGroup = function(name) { + return InjectedScriptHost.releaseObjectGroup(name); }; this._webkitProtocolEnabled = true; @@ -295,6 +303,8 @@ V8Debug.prototype.enableWebkitProtocol = function() { } }; +V8Debug.prototype.releaseObject = +V8Debug.prototype.releaseObjectGroup = V8Debug.prototype.wrapCallFrames = V8Debug.prototype.registerAgentCommand = function(command, parameters, callback) { throw new Error('Use "enableWebkitProtocol" before using this method'); From afdd0577f86f5708f2ca0705280544ff30ca1f4c Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Fri, 8 Jan 2016 05:17:26 +0300 Subject: [PATCH 07/23] Fix tests --- InjectedScript/JavaScriptCallFrame.js | 45 ++++++++++++++++++--------- test/injected-script-source.js | 2 +- test/injected-script.js | 8 ++--- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/InjectedScript/JavaScriptCallFrame.js b/InjectedScript/JavaScriptCallFrame.js index 9a48122..38aa38a 100644 --- a/InjectedScript/JavaScriptCallFrame.js +++ b/InjectedScript/JavaScriptCallFrame.js @@ -13,7 +13,8 @@ setVariableValue: { value: function(scopeNumber, variableName, resolvedValue) { return this.proto.setVariableValue(scopeNumber, variableName, resolvedValue); - } + }, + configurable: true }, caller: { @@ -22,87 +23,101 @@ if (!caller) return; return new JavaScriptCallFrame(caller); - } + }, + configurable: true }, sourceID: { get: function() { return Number(this.proto.sourceID()); - } + }, + configurable: true }, line: { get: function() { return Number(this.proto.line()); - } + }, + configurable: true }, column: { get: function() { return Number(this.proto.column()); - } + }, + configurable: true }, scriptName: { get: function() { return String(this.proto.scriptName()); - } + }, + configurable: true }, functionName: { get: function() { return String(this.proto.functionName()); - } + }, + configurable: true }, functionLine: { get: function() { return Number(this.proto.functionLine()); - } + }, + configurable: true }, functionColumn: { get: function() { return Number(this.proto.functionColumn()); - } + }, + configurable: true }, scopeChain: { get: function() { var scopeChain = this.proto.scopeChain(); return scopeChain.slice(); - } + }, + configurable: true }, scopeType: { value: function(index) { var scopeType = this.proto.scopeType(); return Number(scopeType[index]); - } + }, + configurable: true }, thisObject: { get: function() { return this.proto.thisObject; - } + }, + configurable: true }, stepInPositions: { get: function() { return String(this.proto.stepInPositions()); - } + }, + configurable: true }, isAtReturn: { get: function() { return Boolean(this.proto.isAtReturn); - } + }, + configurable: true }, returnValue: { get: function() { return this.proto.returnValue; - } + }, + configurable: true } }); diff --git a/test/injected-script-source.js b/test/injected-script-source.js index 23f8b88..8a756dc 100644 --- a/test/injected-script-source.js +++ b/test/injected-script-source.js @@ -4,7 +4,7 @@ var NODE_NEXT = require('../tools/NODE_NEXT.js'); if (!NODE_NEXT) return; -describe.only('InjectedScriptSource', function() { +describe('InjectedScriptSource', function() { before(function() { debug.enableWebkitProtocol(); }); diff --git a/test/injected-script.js b/test/injected-script.js index 39421a6..9ae44fd 100644 --- a/test/injected-script.js +++ b/test/injected-script.js @@ -21,6 +21,7 @@ describe('binding', function() { checksTypeValid(new RegExp(), 'regexp'); checksTypeValid(new Error(), 'error'); checksTypeValid(new String(), undefined); + checksTypeValid(new Promise(function() {}), undefined); function checksTypeValid(value, type) { it('checks ' + type + ' subtype', function() { @@ -65,6 +66,9 @@ describe('binding', function() { describe('function `internalConstructorName`', function() { checksNameValid(new Number(), 'Number'); checksNameValid(new Object(), 'Object'); + checksNameValid(new Promise(function() {}), 'Promise'); + checksNameValid(1, undefined); + checksNameValid(null, undefined); function checksNameValid(value, name) { it('checks new ' + name + '() constructor name', function() { @@ -72,10 +76,6 @@ describe('binding', function() { }); } - throwsOnArgs([]); - throwsOnArgs([1]); - throwsOnArgs([null]); - function throwsOnArgs(argvList) { it('should throw on wrong arguments ' + JSON.stringify(argvList), function() { expect(host.internalConstructorName.bind.apply( From 5ad38e5ad3a7058b0fc240edde82653fb722ac41 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Fri, 8 Jan 2016 05:18:08 +0300 Subject: [PATCH 08/23] Add 'emitAgentEvent' method --- v8-debug.js | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/v8-debug.js b/v8-debug.js index 94cde05..eac6c36 100644 --- a/v8-debug.js +++ b/v8-debug.js @@ -145,6 +145,7 @@ V8Debug.prototype._unshareSecurityToken = function() { V8Debug.prototype._wrapDebugCommandProcessor = function() { var proto = this.get('DebugCommandProcessor.prototype'); + this._DebugCommandProcessor = proto; overrides.processDebugRequest_ = proto.processDebugRequest; extend(proto, overrides); overrides.extendedProcessDebugJSONRequestHandles_['disconnect'] = function(request, response) { @@ -154,23 +155,30 @@ V8Debug.prototype._wrapDebugCommandProcessor = function() { }; V8Debug.prototype._unwrapDebugCommandProcessor = function() { - var proto = this.get('DebugCommandProcessor.prototype'); + var proto = this._DebugCommandProcessor; proto.processDebugRequest = proto.processDebugRequest_; delete proto.processDebugRequest_; delete proto.extendedProcessDebugJSONRequest_; delete proto.extendedProcessDebugJSONRequestHandles_; delete proto.extendedProcessDebugJSONRequestAsyncHandles_; + delete this._DebugCommandProcessor; }; V8Debug.prototype.register = V8Debug.prototype.registerCommand = function(name, func) { - var proto = this.get('DebugCommandProcessor.prototype'); + var proto = this._DebugCommandProcessor; proto.extendedProcessDebugJSONRequestHandles_[name] = func; }; +V8Debug.prototype.unregister = +V8Debug.prototype.unregisterCommand = function(name, func) { + var proto = this._DebugCommandProcessor; + delete proto.extendedProcessDebugJSONRequestHandles_[name]; +}; + V8Debug.prototype.registerAsync = V8Debug.prototype.registerAsyncCommand = function(name, func) { - var proto = this.get('DebugCommandProcessor.prototype'); + var proto = this._DebugCommandProcessor; proto.extendedProcessDebugJSONRequestAsyncHandles_[name] = func; }; @@ -196,7 +204,7 @@ V8Debug.prototype.commandToEvent = function(request, response) { }; V8Debug.prototype.registerEvent = function(name) { - var proto = this.get('DebugCommandProcessor.prototype'); + var proto = this._DebugCommandProcessor; proto.extendedProcessDebugJSONRequestHandles_[name] = this.commandToEvent; }; @@ -237,6 +245,8 @@ V8Debug.prototype.enableWebkitProtocol = function() { if (this._webkitProtocolEnabled) return; + var nextTmpEventId = 1; + var DebuggerScriptSource, DebuggerScript, InjectedScriptSource, @@ -266,7 +276,19 @@ V8Debug.prototype.enableWebkitProtocol = function() { parameters = []; } - this.registerCommand(command, new WebkitProtocolCallback(parameters, callback)); + this.registerCommand(command, new WebkitCommandCallback(parameters, callback)); + }; + + this.emitAgentEvent = function(command, callback) { + var handlerName = 'tmpEvent-' + nextTmpEventId++; + this.registerCommand(handlerName, function(request, response) { + this.commandToEvent(request, response); + response.event = command; + + new WebkitEventCallback(callback)(request, response); + }.bind(this)); + this.sendCommand(handlerName); + this.unregisterCommand(handlerName); }; this.wrapCallFrames = function(execState, maximumLimit, scopeDetails) { @@ -288,7 +310,7 @@ V8Debug.prototype.enableWebkitProtocol = function() { this._webkitProtocolEnabled = true; - function WebkitProtocolCallback(argsList, callback) { + function WebkitCommandCallback(argsList, callback) { return function(request, response) { InjectedScriptHost.execState = this.exec_state_; @@ -301,6 +323,16 @@ V8Debug.prototype.enableWebkitProtocol = function() { InjectedScriptHost.execState = null; } } + + function WebkitEventCallback(callback) { + return function(request, response) { + InjectedScriptHost.execState = this.exec_state_; + + callback.call(this, response, injectedScript, DebuggerScript); + + InjectedScriptHost.execState = null; + } + } }; V8Debug.prototype.releaseObject = From 8ad8f0360b2347f8ed42f74deef58b5ac5f0744e Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Sat, 16 Jan 2016 14:45:14 +0300 Subject: [PATCH 09/23] Add method 'disableWebkitProtocol' --- v8-debug.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/v8-debug.js b/v8-debug.js index eac6c36..02ea02a 100644 --- a/v8-debug.js +++ b/v8-debug.js @@ -69,7 +69,7 @@ var overrides = { response = this.createResponse(); } response.success = false; - response.message = e.toString(); + response.message = e.stack; } // Return the response as a JSON encoded string. @@ -86,11 +86,11 @@ var overrides = { '"request_seq":' + request.seq + ',' + '"type":"response",' + '"success":false,' + - '"message":"Internal error: ' + e.toString() + '"}'; + '"message":"Internal error: ' + e.stack + '"}'; } } catch (e) { // Failed in one of the catch blocks above - most generic error. - return '{"seq":0,"type":"response","success":false,"message":"Internal error"}'; + return '{"seq":0,"type":"response","success":false,"message":"' + e.stack + '"}'; } }, processDebugRequest: function WRAPPED_BY_NODE_INSPECTOR(request) { @@ -117,9 +117,7 @@ function V8Debug() { this._unwrapDebugCommandProcessor(); this._unshareSecurityToken(); this._unsetDebugEventListener(); - process.nextTick(function() { - this.removeAllListeners(); - }.bind(this)); + this.disableWebkitProtocol(); }); } @@ -150,7 +148,7 @@ V8Debug.prototype._wrapDebugCommandProcessor = function() { extend(proto, overrides); overrides.extendedProcessDebugJSONRequestHandles_['disconnect'] = function(request, response) { this.emit('close'); - this.processDebugJSONRequest(request); + proto.processDebugJSONRequest(request); }.bind(this); }; @@ -286,9 +284,9 @@ V8Debug.prototype.enableWebkitProtocol = function() { response.event = command; new WebkitEventCallback(callback)(request, response); + this.unregisterCommand(handlerName); }.bind(this)); this.sendCommand(handlerName); - this.unregisterCommand(handlerName); }; this.wrapCallFrames = function(execState, maximumLimit, scopeDetails) { @@ -335,6 +333,13 @@ V8Debug.prototype.enableWebkitProtocol = function() { } }; +V8Debug.prototype.disableWebkitProtocol = function() { + if (!this._webkitProtocolEnabled) return; + this._webkitProtocolEnabled = false; + + this.runInDebugContext('ToggleMirrorCache()'); +}; + V8Debug.prototype.releaseObject = V8Debug.prototype.releaseObjectGroup = V8Debug.prototype.wrapCallFrames = From 82e1a32034ca5f5bdd2d7387a72e67e0a54cfdcd Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Sat, 16 Jan 2016 14:46:31 +0300 Subject: [PATCH 10/23] Remove method 'registerEvent' --- v8-debug.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/v8-debug.js b/v8-debug.js index 02ea02a..fdedc81 100644 --- a/v8-debug.js +++ b/v8-debug.js @@ -8,6 +8,7 @@ var inherits = require('util').inherits; var extend = require('util')._extend; var NODE_NEXT = require('./tools/NODE_NEXT'); +var nextTmpEventId = 1; // Don't cache debugger module delete require.cache[module.id]; @@ -181,8 +182,7 @@ V8Debug.prototype.registerAsyncCommand = function(name, func) { }; V8Debug.prototype.command = -V8Debug.prototype.sendCommand = -V8Debug.prototype.emitEvent = sendCommand; +V8Debug.prototype.sendCommand = sendCommand; function sendCommand(name, attributes, userdata) { var message = { seq: 0, @@ -193,6 +193,17 @@ function sendCommand(name, attributes, userdata) { binding.sendCommand(JSON.stringify(message)); }; +V8Debug.prototype.emitEvent = emitEvent; +function emitEvent(name, attributes, userdata) { + var handlerName = 'tmpEvent-' + nextTmpEventId++; + this.registerCommand(handlerName, function(request, response) { + this.commandToEvent(request, response); + response.event = name; + this.unregisterCommand(handlerName); + }.bind(this)); + this.sendCommand(handlerName, attributes, userdata); +} + V8Debug.prototype.commandToEvent = function(request, response) { response.type = 'event'; response.event = response.command; @@ -201,11 +212,6 @@ V8Debug.prototype.commandToEvent = function(request, response) { delete response.request_seq; }; -V8Debug.prototype.registerEvent = function(name) { - var proto = this._DebugCommandProcessor; - proto.extendedProcessDebugJSONRequestHandles_[name] = this.commandToEvent; -}; - V8Debug.prototype.get = V8Debug.prototype.runInDebugContext = function(script) { if (typeof script == 'function') script = '(' + script.toString() + ')()'; @@ -243,8 +249,6 @@ V8Debug.prototype.enableWebkitProtocol = function() { if (this._webkitProtocolEnabled) return; - var nextTmpEventId = 1; - var DebuggerScriptSource, DebuggerScript, InjectedScriptSource, From 6cdb7e2570eb2a7b507be63eed04d2b8960d6327 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Thu, 21 Jan 2016 18:08:51 +0300 Subject: [PATCH 11/23] Export constructor --- readme.md | 8 +++++++- test/injected-script-source.js | 2 +- test/v8-debug.js | 2 +- v8-debug.js | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/readme.md b/readme.md index b9788f7..e92ff03 100644 --- a/readme.md +++ b/readme.md @@ -11,6 +11,12 @@ This is a part of [node-inspector](http://github.com/node-inspector/node-inspect ``` npm install v8-debug ``` + +## Usage +```js +var debug = require('v8-debug')(); +``` + ## API ### registerCommand(name, callback) @@ -109,7 +115,7 @@ Experimental method for registering WebKit protocol handlers Simple console.log checking ```js -var debug = require('v8-debug'); +var debug = require('v8-debug')(); debug.registerEvent('console.log'); diff --git a/test/injected-script-source.js b/test/injected-script-source.js index 8a756dc..085e503 100644 --- a/test/injected-script-source.js +++ b/test/injected-script-source.js @@ -1,5 +1,5 @@ var expect = require('chai').expect; -var debug = require('../'); +var debug = require('../')(); var NODE_NEXT = require('../tools/NODE_NEXT.js'); if (!NODE_NEXT) return; diff --git a/test/v8-debug.js b/test/v8-debug.js index 86671f6..8b293d0 100644 --- a/test/v8-debug.js +++ b/test/v8-debug.js @@ -1,5 +1,5 @@ var expect = require('chai').expect, - v8debug = require('../'); + v8debug = require('../')(); var NODE_NEXT = require('../tools/NODE_NEXT'); diff --git a/v8-debug.js b/v8-debug.js index fdedc81..3f1142a 100644 --- a/v8-debug.js +++ b/v8-debug.js @@ -103,6 +103,8 @@ var overrides = { inherits(V8Debug, EventEmitter); function V8Debug() { + if (!(this instanceof V8Debug)) return new V8Debug(); + this._webkitProtocolEnabled = false; // NOTE: Call `_setDebugEventListener` before all other changes in Debug Context. @@ -351,4 +353,4 @@ V8Debug.prototype.registerAgentCommand = function(command, parameters, callback) throw new Error('Use "enableWebkitProtocol" before using this method'); }; -module.exports = new V8Debug(); +module.exports = V8Debug; From 2ce348d1b320b4612759b9c9e409afecab5c52f7 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Thu, 21 Jan 2016 18:12:46 +0300 Subject: [PATCH 12/23] Fix v8-debug cleanup --- v8-debug.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/v8-debug.js b/v8-debug.js index 3f1142a..54036a1 100644 --- a/v8-debug.js +++ b/v8-debug.js @@ -10,9 +10,6 @@ var extend = require('util')._extend; var NODE_NEXT = require('./tools/NODE_NEXT'); var nextTmpEventId = 1; -// Don't cache debugger module -delete require.cache[module.id]; - function InjectedScriptDir(link) { return require.resolve(__dirname + '/InjectedScript/' + link); }; @@ -117,10 +114,10 @@ function V8Debug() { this._wrapDebugCommandProcessor(); this.once('close', function() { + this._disableWebkitProtocol(); this._unwrapDebugCommandProcessor(); this._unshareSecurityToken(); this._unsetDebugEventListener(); - this.disableWebkitProtocol(); }); } @@ -147,22 +144,26 @@ V8Debug.prototype._unshareSecurityToken = function() { V8Debug.prototype._wrapDebugCommandProcessor = function() { var proto = this.get('DebugCommandProcessor.prototype'); this._DebugCommandProcessor = proto; - overrides.processDebugRequest_ = proto.processDebugRequest; - extend(proto, overrides); - overrides.extendedProcessDebugJSONRequestHandles_['disconnect'] = function(request, response) { + + if (!proto.processDebugRequest_) { + proto.processDebugRequest_ = proto.processDebugRequest; + extend(proto, overrides); + } + + proto.extendedProcessDebugJSONRequestHandles_['disconnect'] = function(request, response) { this.emit('close'); - proto.processDebugJSONRequest(request); + var proto = this._DebugCommandProcessor; + proto.processDebugJSONRequest(request, response); + return true; }.bind(this); }; V8Debug.prototype._unwrapDebugCommandProcessor = function() { - var proto = this._DebugCommandProcessor; - proto.processDebugRequest = proto.processDebugRequest_; - delete proto.processDebugRequest_; - delete proto.extendedProcessDebugJSONRequest_; - delete proto.extendedProcessDebugJSONRequestHandles_; - delete proto.extendedProcessDebugJSONRequestAsyncHandles_; + var proto = this.get('DebugCommandProcessor.prototype'); delete this._DebugCommandProcessor; + + proto.extendedProcessDebugJSONRequestHandles_ = {}; + proto.extendedProcessDebugJSONRequestAsyncHandles_ = {}; }; V8Debug.prototype.register = @@ -339,11 +340,10 @@ V8Debug.prototype.enableWebkitProtocol = function() { } }; -V8Debug.prototype.disableWebkitProtocol = function() { +V8Debug.prototype._disableWebkitProtocol = function() { if (!this._webkitProtocolEnabled) return; this._webkitProtocolEnabled = false; - - this.runInDebugContext('ToggleMirrorCache()'); + this.runInDebugContext('ToggleMirrorCache(true)'); }; V8Debug.prototype.releaseObject = From b064d0e6d611e6beda9795daf4116836f26e4bd3 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Thu, 21 Jan 2016 18:13:16 +0300 Subject: [PATCH 13/23] Safely register commands --- v8-debug.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/v8-debug.js b/v8-debug.js index 54036a1..2f2e5b9 100644 --- a/v8-debug.js +++ b/v8-debug.js @@ -169,18 +169,24 @@ V8Debug.prototype._unwrapDebugCommandProcessor = function() { V8Debug.prototype.register = V8Debug.prototype.registerCommand = function(name, func) { var proto = this._DebugCommandProcessor; + if (!proto) return; + proto.extendedProcessDebugJSONRequestHandles_[name] = func; }; V8Debug.prototype.unregister = V8Debug.prototype.unregisterCommand = function(name, func) { var proto = this._DebugCommandProcessor; + if (!proto) return; + delete proto.extendedProcessDebugJSONRequestHandles_[name]; }; V8Debug.prototype.registerAsync = V8Debug.prototype.registerAsyncCommand = function(name, func) { var proto = this._DebugCommandProcessor; + if (!proto) return; + proto.extendedProcessDebugJSONRequestAsyncHandles_[name] = func; }; From 40f1871911db4fbff30705fac8b06c1d13e38455 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Sat, 23 Jan 2016 23:10:38 +0300 Subject: [PATCH 14/23] Don't use node-pre-gyp for require binding --- v8-debug.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/v8-debug.js b/v8-debug.js index 2f2e5b9..bf65fad 100644 --- a/v8-debug.js +++ b/v8-debug.js @@ -1,12 +1,19 @@ -var binary = require('node-pre-gyp'); +//var binary = require('node-pre-gyp'); var fs = require('fs'); var path = require('path'); -var binding_path = binary.find(path.resolve(path.join(__dirname,'./package.json'))); -var binding = require(binding_path); +var pack = require('./package.json'); var EventEmitter = require('events').EventEmitter; var inherits = require('util').inherits; var extend = require('util')._extend; +var binding = require('./' + [ + 'build', + 'debug', + 'v' + pack.version, + ['node', 'v' + process.versions.modules, process.platform, process.arch].join('-'), + 'debug.node' +].join('/')); + var NODE_NEXT = require('./tools/NODE_NEXT'); var nextTmpEventId = 1; @@ -152,7 +159,7 @@ V8Debug.prototype._wrapDebugCommandProcessor = function() { proto.extendedProcessDebugJSONRequestHandles_['disconnect'] = function(request, response) { this.emit('close'); - var proto = this._DebugCommandProcessor; + proto._DebugCommandProcessor; proto.processDebugJSONRequest(request, response); return true; }.bind(this); From b46e192b08e032fc9b7131c3e8d4122d73b816d8 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Tue, 26 Jan 2016 01:14:25 +0300 Subject: [PATCH 15/23] Fix tests --- test/{ => next}/injected-script-source.js | 11 ++++++----- test/{ => next}/injected-script.js | 5 +---- test/v8-debug.js | 18 +++++++++++++----- 3 files changed, 20 insertions(+), 14 deletions(-) rename test/{ => next}/injected-script-source.js (97%) rename test/{ => next}/injected-script.js (98%) diff --git a/test/injected-script-source.js b/test/next/injected-script-source.js similarity index 97% rename from test/injected-script-source.js rename to test/next/injected-script-source.js index 085e503..81145f9 100644 --- a/test/injected-script-source.js +++ b/test/next/injected-script-source.js @@ -1,14 +1,15 @@ var expect = require('chai').expect; -var debug = require('../')(); -var NODE_NEXT = require('../tools/NODE_NEXT.js'); - -if (!NODE_NEXT) return; describe('InjectedScriptSource', function() { + var debug = null; + before(function() { - debug.enableWebkitProtocol(); + debug = require('../../')(); + debug.enableWebkitProtocol(); }); + after(function() { debug = null; }); + it('isPrimitiveValue', function(done) { debug.registerAgentCommand('isPrimitiveValue', function(args, response, IScript, DScript) { expect(IScript.isPrimitiveValue(42)).to.be.equal(true); diff --git a/test/injected-script.js b/test/next/injected-script.js similarity index 98% rename from test/injected-script.js rename to test/next/injected-script.js index 9ae44fd..f6def63 100644 --- a/test/injected-script.js +++ b/test/next/injected-script.js @@ -1,10 +1,7 @@ var expect = require('chai').expect; var binary = require('node-pre-gyp'); var path = require('path'); -var binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json'))); -var NODE_NEXT = require('../tools/NODE_NEXT.js'); - -if (!NODE_NEXT) return; +var binding_path = binary.find(path.resolve(path.join(__dirname,'../../package.json'))); describe('binding', function() { var binding = require(binding_path); diff --git a/test/v8-debug.js b/test/v8-debug.js index 8b293d0..fb0cf14 100644 --- a/test/v8-debug.js +++ b/test/v8-debug.js @@ -1,5 +1,4 @@ -var expect = require('chai').expect, - v8debug = require('../')(); +var expect = require('chai').expect; var NODE_NEXT = require('../tools/NODE_NEXT'); @@ -10,13 +9,17 @@ _debugger.stderr.on('data', function(data) { }); describe('v8-debug', function() { + var v8debug = null; + before(function(done) { - _debugger.stdout.on('data', function(data) { - console.log(' ' + data); + v8debug = require('../')() + _debugger.stdout.once('data', function(data) { done(); }); }); + after(function() { v8debug = null; }); + describe('function `runInDebugContext`', function() { it('returns Debug object', function() { var Debug = v8debug.runInDebugContext('Debug'); @@ -55,8 +58,13 @@ describe('v8-debug', function() { describe('events.', function() { it('Emits `close` on disconnect command', function(done) { - v8debug.on('close', done); + v8debug.once('close', done); v8debug.sendCommand('disconnect'); }); }); }); + +if (NODE_NEXT) { + require('./next/injected-script.js'); + require('./next/injected-script-source.js'); +} From 13af6615146a8282d9388c05630c51bc907be82d Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Tue, 26 Jan 2016 01:23:13 +0300 Subject: [PATCH 16/23] Fix 0.10 build --- src/debug.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/debug.cc b/src/debug.cc index 4149c09..61c5091 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -59,7 +59,7 @@ namespace nodex { if (expression.IsEmpty()) RETURN(Undefined()); - Local current_context = Isolate::GetCurrent()->GetCurrentContext(); + Local current_context = Nan::GetCurrentContext(); Local debug_context = v8::Debug::GetDebugContext(); #if (NODE_MODULE_VERSION > 45) if (debug_context.IsEmpty()) { @@ -89,7 +89,7 @@ namespace nodex { } static NAN_METHOD(ShareSecurityToken) { - Local current_context = info.GetIsolate()->GetCurrentContext(); + Local current_context = Nan::GetCurrentContext(); Local debug_context = v8::Debug::GetDebugContext(); #if (NODE_MODULE_VERSION > 45) if (debug_context.IsEmpty()) { @@ -103,7 +103,7 @@ namespace nodex { } static NAN_METHOD(UnshareSecurityToken) { - Local current_context = info.GetIsolate()->GetCurrentContext(); + Local current_context = Nan::GetCurrentContext(); Local debug_context = v8::Debug::GetDebugContext(); #if (NODE_MODULE_VERSION > 45) if (debug_context.IsEmpty()) { From d907cc59e076926676ae6c4d22e9e472a43a745d Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Tue, 26 Jan 2016 01:29:43 +0300 Subject: [PATCH 17/23] Added method setPauseOnNextStatement --- src/debug.cc | 12 ++++++++++++ v8-debug.js | 20 ++++++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/debug.cc b/src/debug.cc index 61c5091..64521d6 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -10,6 +10,7 @@ using v8::Isolate; using v8::Local; using v8::Value; +using v8::Boolean; using v8::String; using v8::Object; using v8::Context; @@ -114,6 +115,16 @@ namespace nodex { #endif debug_context->UseDefaultSecurityToken(); } + + static NAN_METHOD(SetPauseOnNextStatement) { + Local _pause = CHK(To(info[0])); + bool pause = _pause->Value(); + if (pause) + v8::Debug::DebugBreak(info.GetIsolate()); + else + v8::Debug::CancelDebugBreak(info.GetIsolate()); + } + private: Debug() {} ~Debug() {} @@ -131,6 +142,7 @@ namespace nodex { SetMethod(target, "allowNatives", Debug::AllowNatives); SetMethod(target, "shareSecurityToken", Debug::ShareSecurityToken); SetMethod(target, "unshareSecurityToken", Debug::UnshareSecurityToken); + SetMethod(target, "setPauseOnNextStatement", Debug::SetPauseOnNextStatement); } NODE_MODULE(debug, Initialize) diff --git a/v8-debug.js b/v8-debug.js index bf65fad..36f3c68 100644 --- a/v8-debug.js +++ b/v8-debug.js @@ -109,6 +109,8 @@ inherits(V8Debug, EventEmitter); function V8Debug() { if (!(this instanceof V8Debug)) return new V8Debug(); + this._Debug = this.get('Debug'); + this._webkitProtocolEnabled = false; // NOTE: Call `_setDebugEventListener` before all other changes in Debug Context. @@ -129,15 +131,13 @@ function V8Debug() { } V8Debug.prototype._setDebugEventListener = function() { - var Debug = this.get('Debug'); - Debug.setListener(function(_, execState, event) { + this._Debug.setListener(function(_, execState, event) { // TODO(3y3): Handle events here }); }; V8Debug.prototype._unsetDebugEventListener = function() { - var Debug = this.get('Debug'); - Debug.setListener(null); + this._Debug.setListener(null); }; V8Debug.prototype._shareSecurityToken = function() { @@ -160,8 +160,8 @@ V8Debug.prototype._wrapDebugCommandProcessor = function() { proto.extendedProcessDebugJSONRequestHandles_['disconnect'] = function(request, response) { this.emit('close'); proto._DebugCommandProcessor; - proto.processDebugJSONRequest(request, response); - return true; + //proto.processDebugJSONRequest(request, response); + //return true; }.bind(this); }; @@ -173,6 +173,14 @@ V8Debug.prototype._unwrapDebugCommandProcessor = function() { proto.extendedProcessDebugJSONRequestAsyncHandles_ = {}; }; +V8Debug.prototype.setPauseOnNextStatement = function(pause) { + binding.setPauseOnNextStatement(pause === true); +}; + +V8Debug.prototype.scripts = function() { + return this._Debug.scripts(); +}; + V8Debug.prototype.register = V8Debug.prototype.registerCommand = function(name, func) { var proto = this._DebugCommandProcessor; From 8f0555f2dde2f60390dd684a656f0a3c11c100e9 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Wed, 3 Feb 2016 18:28:30 +0300 Subject: [PATCH 18/23] Implement STR in tools --- src/JavaScriptCallFrame.cc | 9 ++++----- src/tools.h | 3 +++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/JavaScriptCallFrame.cc b/src/JavaScriptCallFrame.cc index f73fc1a..0d6820e 100644 --- a/src/JavaScriptCallFrame.cc +++ b/src/JavaScriptCallFrame.cc @@ -15,7 +15,6 @@ using v8::Function; using Nan::To; using Nan::New; using Nan::Get; -using Nan::Set; using Nan::SetMethod; using Nan::EscapableHandleScope; using Nan::Undefined; @@ -53,8 +52,8 @@ namespace nodex { }; NAN_METHOD(JavaScriptCallFrame::EvaluateWithExceptionDetails) { - Local callFrame = CHK(To(CHK(Get(info.Holder(), CHK(New("proto")))))); - Local evalFunction = Local::Cast(CHK(Get(callFrame, CHK(New("evaluate"))))); + Local callFrame = CHK(To(CHK(Get(info.Holder(), STR("proto"))))); + Local evalFunction = Local::Cast(CHK(Get(callFrame, STR("evaluate")))); Local expression = info[0]; Local scopeExtension = info[1]; @@ -82,8 +81,8 @@ namespace nodex { }; NAN_METHOD(JavaScriptCallFrame::Restart) { - Local callFrame = CHK(To(CHK(Get(info.Holder(), CHK(New("proto")))))); - Local restartFunction = Local::Cast(CHK(Get(callFrame, CHK(New("restart"))))); + Local callFrame = CHK(To(CHK(Get(info.Holder(), STR("proto"))))); + Local restartFunction = Local::Cast(CHK(Get(callFrame, STR("restart")))); TryCatch tryCatch; MaybeLocal result; diff --git a/src/tools.h b/src/tools.h index 3b70899..b1f1b9f 100644 --- a/src/tools.h +++ b/src/tools.h @@ -4,6 +4,9 @@ #define CHK(VALUE) \ VALUE.ToLocalChecked() +#define STR(VALUE) \ + Nan::New(VALUE).ToLocalChecked() + #define RETURN(VALUE) { \ info.GetReturnValue().Set(VALUE); \ return; \ From 59f8ce50da04359bb429b54e3531ce00578a63b4 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Wed, 3 Feb 2016 18:29:18 +0300 Subject: [PATCH 19/23] Check empty call stack --- v8-debug.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/v8-debug.js b/v8-debug.js index 36f3c68..52455bf 100644 --- a/v8-debug.js +++ b/v8-debug.js @@ -323,6 +323,8 @@ V8Debug.prototype.enableWebkitProtocol = function() { if (maximumLimit < 0) throw new Error('Incorrect stack trace limit.'); var data = (maximumLimit << scopeBits) | scopeDetails; var currentCallFrame = DebuggerScript.currentCallFrame(execState, data); + if (!currentCallFrame) return; + return new JavaScriptCallFrame(currentCallFrame); }; From 8b8c6159dd6e10c6805c1fa8fef027e935b928a4 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Fri, 12 Feb 2016 11:42:02 +0300 Subject: [PATCH 20/23] Added 'setLiveEditEnabled' method --- src/debug.cc | 9 +++++++++ v8-debug.js | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/src/debug.cc b/src/debug.cc index 64521d6..9426227 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -125,6 +125,14 @@ namespace nodex { v8::Debug::CancelDebugBreak(info.GetIsolate()); } + static NAN_METHOD(SetLiveEditEnabled) { +#if (NODE_MODULE_VERSION > 45) + Local _enabled = CHK(To(info[0])); + bool enabled = _enabled->Value(); + v8::Debug::SetLiveEditEnabled(info.GetIsolate(), enabled); +#endif + } + private: Debug() {} ~Debug() {} @@ -143,6 +151,7 @@ namespace nodex { SetMethod(target, "shareSecurityToken", Debug::ShareSecurityToken); SetMethod(target, "unshareSecurityToken", Debug::UnshareSecurityToken); SetMethod(target, "setPauseOnNextStatement", Debug::SetPauseOnNextStatement); + SetMethod(target, "setLiveEditEnabled", Debug::SetLiveEditEnabled); } NODE_MODULE(debug, Initialize) diff --git a/v8-debug.js b/v8-debug.js index 52455bf..2ae69bc 100644 --- a/v8-debug.js +++ b/v8-debug.js @@ -177,6 +177,10 @@ V8Debug.prototype.setPauseOnNextStatement = function(pause) { binding.setPauseOnNextStatement(pause === true); }; +V8Debug.prototype.setLiveEditEnabled = function(enabled) { + binding.setLiveEditEnabled(enabled === true); +}; + V8Debug.prototype.scripts = function() { return this._Debug.scripts(); }; From a4606e83dbb6557765164ca6c6df25987fd42e5e Mon Sep 17 00:00:00 2001 From: Jason Date: Sun, 14 Feb 2016 16:10:35 +0800 Subject: [PATCH 21/23] Dont mutate liveEditEnabled state in CallFrame::Restart --- src/JavaScriptCallFrame.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/JavaScriptCallFrame.cc b/src/JavaScriptCallFrame.cc index 0d6820e..9f8f64b 100644 --- a/src/JavaScriptCallFrame.cc +++ b/src/JavaScriptCallFrame.cc @@ -87,9 +87,7 @@ namespace nodex { TryCatch tryCatch; MaybeLocal result; - v8::Debug::SetLiveEditEnabled(info.GetIsolate(), true); result = restartFunction->Call(callFrame, 0, NULL); - v8::Debug::SetLiveEditEnabled(info.GetIsolate(), false); MAYBE_RETHROW(); RETURN(CHK(result)); From 8a273cc04e36f2ca124c740a03e65fe00b881028 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Thu, 28 Apr 2016 01:06:23 +0300 Subject: [PATCH 22/23] Exclude 4.0.0-win32-ia32 from prebuilts --- appveyor.yml | 6 ++++-- tools/prepublish-to-npm.js | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 86c9a91..e6fd390 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,14 +5,16 @@ environment: secure: jJxKoyWputzRz2EjAT9i/vBzYt2+lcjKZ5D6O5TBaS9+anpYHn2XWbOut5dkOgh0 node_pre_gyp_region: eu-central-1 matrix: + - NODE_VERSION: 6 + platform: x64 + - NODE_VERSION: 6 + platform: x86 - NODE_VERSION: 5 platform: x64 - NODE_VERSION: 5 platform: x86 - NODE_VERSION: 4 platform: x64 - - NODE_VERSION: 4 - platform: x86 - NODE_VERSION: 0.12 platform: x64 - NODE_VERSION: 0.12 diff --git a/tools/prepublish-to-npm.js b/tools/prepublish-to-npm.js index 55f145d..5abc566 100644 --- a/tools/prepublish-to-npm.js +++ b/tools/prepublish-to-npm.js @@ -7,13 +7,17 @@ rimraf.sync('./build'); var versions = ['0.10.0', '0.12.0', '4.0.0', '5.0.0']; var matrix = { x64: ['win32', 'linux', 'darwin'], - ia32: ['win32'] + ia32: ['win32'], + except: { '4.0.0-win32-ia32': true } }; var targets = []; Object.keys(matrix).forEach(function(arch) { matrix[arch].forEach(function(platform) { versions.forEach(function(version) { + var key = version + '-' + platform + '-' + arch; + if (matrix.except[key]) return; + targets.push({ target: version, target_platform: platform, From cc8defd93f703d8a2f4d0d0633091e654e05c1f6 Mon Sep 17 00:00:00 2001 From: Yury Puzynya <3y3@bk.ru> Date: Thu, 28 Apr 2016 01:07:18 +0300 Subject: [PATCH 23/23] Update nan --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 21da994..cd59c3f 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ }, "main": "v8-debug", "dependencies": { - "nan": "^2.0.4", + "nan": "^2.3.2", "node-pre-gyp": "^0.6.5" }, "devDependencies": {