Skip to content

Commit 39a2fc0

Browse files
committed
Implement 'JavaScriptCallFrame'
1 parent 0226457 commit 39a2fc0

File tree

7 files changed

+247
-4
lines changed

7 files changed

+247
-4
lines changed

InjectedScript/JavaScriptCallFrame.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
"use strict";
2+
3+
(function(binding) {
4+
function JavaScriptCallFrame(proto) {
5+
Object.defineProperty(this, 'proto', {
6+
get: function() { return proto; }
7+
});
8+
}
9+
10+
JavaScriptCallFrame.prototype = binding.JavaScriptCallFrame;
11+
12+
Object.defineProperties(JavaScriptCallFrame.prototype, {
13+
setVariableValue: {
14+
value: function(scopeNumber, variableName, resolvedValue) {
15+
return this.proto.setVariableValue(scopeNumber, variableName, resolvedValue);
16+
}
17+
},
18+
19+
caller: {
20+
get: function() {
21+
var caller = this.proto.caller;
22+
if (!caller) return;
23+
24+
return new JavaScriptCallFrame(caller);
25+
}
26+
},
27+
28+
sourceID: {
29+
get: function() {
30+
return Number(this.proto.sourceID());
31+
}
32+
},
33+
34+
line: {
35+
get: function() {
36+
return Number(this.proto.line());
37+
}
38+
},
39+
40+
column: {
41+
get: function() {
42+
return Number(this.proto.column());
43+
}
44+
},
45+
46+
scriptName: {
47+
get: function() {
48+
return String(this.proto.scriptName());
49+
}
50+
},
51+
52+
functionName: {
53+
get: function() {
54+
return String(this.proto.functionName());
55+
}
56+
},
57+
58+
functionLine: {
59+
get: function() {
60+
return Number(this.proto.functionLine());
61+
}
62+
},
63+
64+
functionColumn: {
65+
get: function() {
66+
return Number(this.proto.functionColumn());
67+
}
68+
},
69+
70+
scopeChain: {
71+
get: function() {
72+
var scopeChain = this.proto.scopeChain();
73+
return scopeChain.slice();
74+
}
75+
},
76+
77+
scopeType: {
78+
value: function(index) {
79+
var scopeType = this.proto.scopeType();
80+
return Number(scopeType[index]);
81+
}
82+
},
83+
84+
thisObject: {
85+
get: function() {
86+
return this.proto.thisObject;
87+
}
88+
},
89+
90+
stepInPositions: {
91+
get: function() {
92+
return String(this.proto.stepInPositions());
93+
}
94+
},
95+
96+
isAtReturn: {
97+
get: function() {
98+
return Boolean(this.proto.isAtReturn);
99+
}
100+
},
101+
102+
returnValue: {
103+
get: function() {
104+
return this.proto.returnValue;
105+
}
106+
}
107+
});
108+
109+
return JavaScriptCallFrame;
110+
});

binding.gyp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
'conditions' : [
1616
['node_next=="true"', {
1717
'sources': [
18-
'src/InjectedScriptHost.cc'
18+
'src/InjectedScriptHost.cc',
19+
'src/JavaScriptCallFrame.cc'
1920
],
2021
'defines': ['NODE_NEXT=1']
2122
}]

src/JavaScriptCallFrame.cc

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#include <v8-debug.h>
2+
3+
#include "JavaScriptCallFrame.h"
4+
#include "tools.h"
5+
6+
using v8::Isolate;
7+
using v8::Local;
8+
using v8::Value;
9+
using v8::Number;
10+
using v8::Integer;
11+
using v8::String;
12+
using v8::Object;
13+
using v8::Message;
14+
using v8::Function;
15+
using Nan::To;
16+
using Nan::New;
17+
using Nan::Get;
18+
using Nan::Set;
19+
using Nan::SetMethod;
20+
using Nan::EscapableHandleScope;
21+
using Nan::Undefined;
22+
using Nan::TryCatch;
23+
using Nan::ThrowError;
24+
using Nan::ThrowTypeError;
25+
using Nan::MaybeLocal;
26+
27+
namespace nodex {
28+
void JavaScriptCallFrame::Initialize(Local<Object> target) {
29+
Local<Object> javaScriptCallFrame = New<Object>();
30+
SetMethod(javaScriptCallFrame, "evaluateWithExceptionDetails", EvaluateWithExceptionDetails);
31+
SetMethod(javaScriptCallFrame, "restart", Restart);
32+
33+
SET(target, "JavaScriptCallFrame", javaScriptCallFrame);
34+
}
35+
36+
Local<Object> JavaScriptCallFrame::createExceptionDetails(Local<Message> message) {
37+
EscapableHandleScope scope;
38+
39+
Local<Object> exceptionDetails = New<Object>();
40+
SET(exceptionDetails, "text", message->Get());
41+
42+
SET(exceptionDetails, "url", message->GetScriptOrigin().ResourceName());
43+
SET(exceptionDetails, "scriptId", New<Integer>((int32_t)message->GetScriptOrigin().ScriptID()->Value()));
44+
SET(exceptionDetails, "line", New<Integer>(message->GetLineNumber()));
45+
SET(exceptionDetails, "column", New<Number>(message->GetStartColumn()));
46+
47+
if (!message->GetStackTrace().IsEmpty())
48+
SET(exceptionDetails, "stackTrace", message->GetStackTrace()->AsArray());
49+
else
50+
SET(exceptionDetails, "stackTrace", Undefined());
51+
52+
return scope.Escape(exceptionDetails);
53+
};
54+
55+
NAN_METHOD(JavaScriptCallFrame::EvaluateWithExceptionDetails) {
56+
Local<Object> callFrame = CHK(To<Object>(CHK(Get(info.Holder(), CHK(New("proto"))))));
57+
Local<Function> evalFunction = Local<Function>::Cast(CHK(Get(callFrame, CHK(New("evaluate")))));
58+
59+
Local<Value> expression = info[0];
60+
Local<Value> scopeExtension = info[1];
61+
v8::Local<v8::Value> argv[] = {
62+
expression,
63+
scopeExtension
64+
};
65+
66+
Local<Object> wrappedResult = New<Object>();
67+
68+
TryCatch tryCatch;
69+
MaybeLocal<Value> result;
70+
71+
result = evalFunction->Call(callFrame, 2, argv);
72+
73+
if (tryCatch.HasCaught()) {
74+
SET(wrappedResult, "result", tryCatch.Exception());
75+
SET(wrappedResult, "exceptionDetails", createExceptionDetails(tryCatch.Message()));
76+
} else {
77+
SET(wrappedResult, "result", CHK(result));
78+
SET(wrappedResult, "exceptionDetails", Undefined());
79+
}
80+
81+
RETURN(wrappedResult);
82+
};
83+
84+
NAN_METHOD(JavaScriptCallFrame::Restart) {
85+
Local<Object> callFrame = CHK(To<Object>(CHK(Get(info.Holder(), CHK(New("proto"))))));
86+
Local<Function> restartFunction = Local<Function>::Cast(CHK(Get(callFrame, CHK(New("restart")))));
87+
88+
TryCatch tryCatch;
89+
MaybeLocal<Value> result;
90+
91+
v8::Debug::SetLiveEditEnabled(info.GetIsolate(), true);
92+
result = restartFunction->Call(callFrame, 0, NULL);
93+
v8::Debug::SetLiveEditEnabled(info.GetIsolate(), false);
94+
95+
MAYBE_RETHROW();
96+
RETURN(CHK(result));
97+
};
98+
}

src/JavaScriptCallFrame.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#ifndef X_JAVASCRIPT_CALL_FRAME_
2+
#define X_JAVASCRIPT_CALL_FRAME_
3+
4+
#include <nan.h>
5+
6+
namespace nodex {
7+
8+
class JavaScriptCallFrame {
9+
public:
10+
static void Initialize(v8::Local<v8::Object> target);
11+
static NAN_METHOD(EvaluateWithExceptionDetails);
12+
static NAN_METHOD(Restart);
13+
private:
14+
static v8::Local<v8::Object> createExceptionDetails(v8::Local<v8::Message> message);
15+
};
16+
17+
} //namespace nodex
18+
#endif // X_JAVASCRIPT_CALL_FRAME_

src/debug.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "tools.h"
55
#if (NODE_NEXT)
66
#include "InjectedScriptHost.h"
7+
#include "JavaScriptCallFrame.h"
78
#endif
89

910
using v8::Isolate;
@@ -121,6 +122,7 @@ namespace nodex {
121122
NAN_MODULE_INIT(Initialize) {
122123
#if (NODE_NEXT)
123124
InjectedScriptHost::Initialize(target);
125+
JavaScriptCallFrame::Initialize(target);
124126
#endif
125127

126128
SetMethod(target, "call", Debug::Call);

src/tools.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414

1515
#define RUNSCRIPT(EXPRESSION, RESULT) while (true) { \
1616
Nan::MaybeLocal<Nan::BoundScript> script = Nan::CompileScript(EXPRESSION);\
17-
if (tryCatch.HasCaught()) break; \
17+
if (tryCatch.HasCaught()) break; \
1818
RESULT = Nan::RunScript(CHK(script)); \
1919
break; \
2020
}
2121

2222
#define MAYBE_RETHROW() \
23-
if (tryCatch.HasCaught()) { \
24-
tryCatch.ReThrow(); \
23+
if (tryCatch.HasCaught()) { \
24+
tryCatch.ReThrow(); \
2525
return; \
2626
}
2727

v8-debug.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ function InjectedScriptDir(link) {
1818
var DebuggerScriptLink = InjectedScriptDir('DebuggerScript.js');
1919
var InjectedScriptLink = InjectedScriptDir('InjectedScriptSource.js');
2020
var InjectedScriptHostLink = InjectedScriptDir('InjectedScriptHost.js');
21+
var JavaScriptCallFrameLink = InjectedScriptDir('JavaScriptCallFrame.js');
2122

2223
var overrides = {
2324
extendedProcessDebugJSONRequestHandles_: {},
@@ -254,6 +255,9 @@ V8Debug.prototype.enableWebkitProtocol = function() {
254255
InjectedScriptHostSource = fs.readFileSync(InjectedScriptHostLink, 'utf8');
255256
InjectedScriptHost = this.runInDebugContext(InjectedScriptHostSource)(binding, DebuggerScript);
256257

258+
JavaScriptCallFrameSource = fs.readFileSync(JavaScriptCallFrameLink, 'utf8');
259+
JavaScriptCallFrame = this.runInDebugContext(JavaScriptCallFrameSource)(binding, DebuggerScript);
260+
257261
var injectedScript = InjectedScript(InjectedScriptHost, global, 1);
258262

259263
this.registerAgentCommand = function(command, parameters, callback) {
@@ -265,6 +269,15 @@ V8Debug.prototype.enableWebkitProtocol = function() {
265269
this.registerCommand(command, new WebkitProtocolCallback(parameters, callback));
266270
};
267271

272+
this.wrapCallFrames = function(execState, maximumLimit, scopeDetails) {
273+
var scopeBits = 2;
274+
275+
if (maximumLimit < 0) throw new Error('Incorrect stack trace limit.');
276+
var data = (maximumLimit << scopeBits) | scopeDetails;
277+
278+
return JavaScriptCallFrame.currentCallFrame(execState, data);;
279+
};
280+
268281
this._webkitProtocolEnabled = true;
269282

270283
function WebkitProtocolCallback(argsList, callback) {
@@ -282,6 +295,7 @@ V8Debug.prototype.enableWebkitProtocol = function() {
282295
}
283296
};
284297

298+
V8Debug.prototype.wrapCallFrames =
285299
V8Debug.prototype.registerAgentCommand = function(command, parameters, callback) {
286300
throw new Error('Use "enableWebkitProtocol" before using this method');
287301
};

0 commit comments

Comments
 (0)