11#include " inspector_agent.h"
22
33#include " inspector_io.h"
4- #include " base-object.h"
5- #include " base-object-inl.h"
64#include " node_internals.h"
75#include " v8-inspector.h"
86#include " v8-platform.h"
9- #include " zlib.h"
107
118#include " libplatform/libplatform.h"
129
1613#include < vector>
1714
1815#ifdef __POSIX__
16+ #include < limits.h>
1917#include < unistd.h> // setuid, getuid
2018#endif // __POSIX__
2119
@@ -26,23 +24,12 @@ namespace {
2624using node::FatalError;
2725
2826using v8::Array;
29- using v8::Boolean;
3027using v8::Context;
3128using v8::Function;
32- using v8::FunctionCallbackInfo;
33- using v8::FunctionTemplate;
3429using v8::HandleScope;
35- using v8::Integer;
3630using v8::Isolate;
3731using v8::Local;
38- using v8::Maybe;
39- using v8::MaybeLocal;
40- using v8::Name;
41- using v8::NewStringType;
4232using v8::Object;
43- using v8::Persistent;
44- using v8::String;
45- using v8::Undefined;
4633using v8::Value;
4734
4835using v8_inspector::StringBuffer;
@@ -193,173 +180,6 @@ static int StartDebugSignalHandler() {
193180}
194181#endif // _WIN32
195182
196- class JSBindingsConnection : public AsyncWrap {
197- public:
198- class JSBindingsSessionDelegate : public InspectorSessionDelegate {
199- public:
200- JSBindingsSessionDelegate (Environment* env,
201- JSBindingsConnection* connection)
202- : env_(env),
203- connection_ (connection) {
204- }
205-
206- bool WaitForFrontendMessageWhilePaused () override {
207- return false ;
208- }
209-
210- void SendMessageToFrontend (const v8_inspector::StringView& message)
211- override {
212- Isolate* isolate = env_->isolate ();
213- HandleScope handle_scope (isolate);
214- Context::Scope context_scope (env_->context ());
215- MaybeLocal<String> v8string =
216- String::NewFromTwoByte (isolate, message.characters16 (),
217- NewStringType::kNormal , message.length ());
218- Local<Value> argument = v8string.ToLocalChecked ().As <Value>();
219- connection_->OnMessage (argument);
220- }
221-
222- void Disconnect () {
223- Agent* agent = env_->inspector_agent ();
224- if (agent->delegate () == this )
225- agent->Disconnect ();
226- }
227-
228- private:
229- Environment* env_;
230- JSBindingsConnection* connection_;
231- };
232-
233- JSBindingsConnection (Environment* env,
234- Local<Object> wrap,
235- Local<Function> callback)
236- : AsyncWrap(env, wrap, PROVIDER_INSPECTORJSBINDING),
237- delegate_(env, this ),
238- callback_(env->isolate (), callback) {
239- Wrap (wrap, this );
240-
241- Agent* inspector = env->inspector_agent ();
242- if (inspector->delegate () != nullptr ) {
243- env->ThrowTypeError (" Session is already attached" );
244- return ;
245- }
246- inspector->Connect (&delegate_);
247- }
248-
249- ~JSBindingsConnection () override {
250- callback_.Reset ();
251- }
252-
253- void OnMessage (Local<Value> value) {
254- MakeCallback (callback_.Get (env ()->isolate ()), 1 , &value);
255- }
256-
257- void CheckIsCurrent () {
258- Agent* inspector = env ()->inspector_agent ();
259- CHECK_EQ (&delegate_, inspector->delegate ());
260- }
261-
262- static void New (const FunctionCallbackInfo<Value>& info) {
263- Environment* env = Environment::GetCurrent (info);
264- if (!info[0 ]->IsFunction ()) {
265- env->ThrowTypeError (" Message callback is required" );
266- return ;
267- }
268- Local<Function> callback = info[0 ].As <Function>();
269- new JSBindingsConnection (env, info.This (), callback);
270- }
271-
272- void Disconnect () {
273- delegate_.Disconnect ();
274- if (!persistent ().IsEmpty ()) {
275- ClearWrap (object ());
276- persistent ().Reset ();
277- }
278- delete this ;
279- }
280-
281- static void Disconnect (const FunctionCallbackInfo<Value>& info) {
282- JSBindingsConnection* session;
283- ASSIGN_OR_RETURN_UNWRAP (&session, info.Holder ());
284- session->Disconnect ();
285- }
286-
287- static void Dispatch (const FunctionCallbackInfo<Value>& info) {
288- Environment* env = Environment::GetCurrent (info);
289- JSBindingsConnection* session;
290- ASSIGN_OR_RETURN_UNWRAP (&session, info.Holder ());
291- if (!info[0 ]->IsString ()) {
292- env->ThrowTypeError (" Inspector message must be a string" );
293- return ;
294- }
295-
296- session->CheckIsCurrent ();
297- Agent* inspector = env->inspector_agent ();
298- inspector->Dispatch (ToProtocolString (env->isolate (), info[0 ])->string ());
299- }
300-
301- size_t self_size () const override { return sizeof (*this ); }
302-
303- private:
304- JSBindingsSessionDelegate delegate_;
305- Persistent<Function> callback_;
306- };
307-
308- void InspectorConsoleCall (const v8::FunctionCallbackInfo<Value>& info) {
309- Isolate* isolate = info.GetIsolate ();
310- HandleScope handle_scope (isolate);
311- Local<Context> context = isolate->GetCurrentContext ();
312- CHECK_LT (2 , info.Length ());
313- std::vector<Local<Value>> call_args;
314- for (int i = 3 ; i < info.Length (); ++i) {
315- call_args.push_back (info[i]);
316- }
317- Environment* env = Environment::GetCurrent (isolate);
318- if (env->inspector_agent ()->enabled ()) {
319- Local<Value> inspector_method = info[0 ];
320- CHECK (inspector_method->IsFunction ());
321- Local<Value> config_value = info[2 ];
322- CHECK (config_value->IsObject ());
323- Local<Object> config_object = config_value.As <Object>();
324- Local<String> in_call_key = FIXED_ONE_BYTE_STRING (isolate, " in_call" );
325- if (!config_object->Has (context, in_call_key).FromMaybe (false )) {
326- CHECK (config_object->Set (context,
327- in_call_key,
328- v8::True (isolate)).FromJust ());
329- CHECK (!inspector_method.As <Function>()->Call (context,
330- info.Holder (),
331- call_args.size (),
332- call_args.data ()).IsEmpty ());
333- }
334- CHECK (config_object->Delete (context, in_call_key).FromJust ());
335- }
336-
337- Local<Value> node_method = info[1 ];
338- CHECK (node_method->IsFunction ());
339- node_method.As <Function>()->Call (context,
340- info.Holder (),
341- call_args.size (),
342- call_args.data ()).FromMaybe (Local<Value>());
343- }
344-
345- void CallAndPauseOnStart (
346- const v8::FunctionCallbackInfo<v8::Value>& args) {
347- Environment* env = Environment::GetCurrent (args);
348- CHECK_GT (args.Length (), 1 );
349- CHECK (args[0 ]->IsFunction ());
350- std::vector<v8::Local<v8::Value>> call_args;
351- for (int i = 2 ; i < args.Length (); i++) {
352- call_args.push_back (args[i]);
353- }
354-
355- env->inspector_agent ()->PauseOnNextJavascriptStatement (" Break on start" );
356- v8::MaybeLocal<v8::Value> retval =
357- args[0 ].As <v8::Function>()->Call (env->context (), args[1 ],
358- call_args.size (), call_args.data ());
359- if (!retval.IsEmpty ()) {
360- args.GetReturnValue ().Set (retval.ToLocalChecked ());
361- }
362- }
363183
364184// Used in NodeInspectorClient::currentTimeMS() below.
365185const int NANOS_PER_MSEC = 1000000 ;
@@ -710,20 +530,6 @@ bool Agent::StartIoThread(bool wait_for_connect) {
710530 return true ;
711531}
712532
713- static void AddCommandLineAPI (
714- const FunctionCallbackInfo<Value>& info) {
715- auto env = Environment::GetCurrent (info);
716- Local<Context> context = env->context ();
717-
718- if (info.Length () != 2 || !info[0 ]->IsString ()) {
719- return env->ThrowTypeError (" inspector.addCommandLineAPI takes "
720- " exactly 2 arguments: a string and a value." );
721- }
722-
723- Local<Object> console_api = env->inspector_console_api_object ();
724- console_api->Set (context, info[0 ], info[1 ]).FromJust ();
725- }
726-
727533void Agent::Stop () {
728534 if (io_ != nullptr ) {
729535 io_->Stop ();
@@ -842,138 +648,6 @@ void Agent::AllAsyncTasksCanceled() {
842648 client_->AllAsyncTasksCanceled ();
843649}
844650
845- void Open (const FunctionCallbackInfo<Value>& args) {
846- Environment* env = Environment::GetCurrent (args);
847- inspector::Agent* agent = env->inspector_agent ();
848- bool wait_for_connect = false ;
849-
850- if (args.Length () > 0 && args[0 ]->IsUint32 ()) {
851- uint32_t port = args[0 ]->Uint32Value ();
852- agent->options ().set_port (static_cast <int >(port));
853- }
854-
855- if (args.Length () > 1 && args[1 ]->IsString ()) {
856- node::Utf8Value host (env->isolate (), args[1 ].As <String>());
857- agent->options ().set_host_name (*host);
858- }
859-
860- if (args.Length () > 2 && args[2 ]->IsBoolean ()) {
861- wait_for_connect = args[2 ]->BooleanValue ();
862- }
863-
864- agent->StartIoThread (wait_for_connect);
865- }
866-
867- void Url (const FunctionCallbackInfo<Value>& args) {
868- Environment* env = Environment::GetCurrent (args);
869- inspector::Agent* agent = env->inspector_agent ();
870- inspector::InspectorIo* io = agent->io ();
871-
872- if (!io) return ;
873-
874- std::vector<std::string> ids = io->GetTargetIds ();
875-
876- if (ids.empty ()) return ;
877-
878- std::string url = FormatWsAddress (io->host (), io->port (), ids[0 ], true );
879- args.GetReturnValue ().Set (OneByteString (env->isolate (), url.c_str ()));
880- }
881-
882- static void * GetAsyncTask (int64_t asyncId) {
883- // The inspector assumes that when other clients use its asyncTask* API,
884- // they use real pointers, or at least something aligned like real pointer.
885- // In general it means that our task_id should always be even.
886- //
887- // On 32bit platforms, the 64bit asyncId would get truncated when converted
888- // to a 32bit pointer. However, the javascript part will never enable
889- // the async_hook on 32bit platforms, therefore the truncation will never
890- // happen in practice.
891- return reinterpret_cast <void *>(asyncId << 1 );
892- }
893-
894- template <void (Agent::*asyncTaskFn)(void *)>
895- static void InvokeAsyncTaskFnWithId (const FunctionCallbackInfo<Value>& args) {
896- Environment* env = Environment::GetCurrent (args);
897- CHECK (args[0 ]->IsNumber ());
898- int64_t task_id = args[0 ]->IntegerValue (env->context ()).FromJust ();
899- (env->inspector_agent ()->*asyncTaskFn)(GetAsyncTask (task_id));
900- }
901-
902- static void AsyncTaskScheduledWrapper (const FunctionCallbackInfo<Value>& args) {
903- Environment* env = Environment::GetCurrent (args);
904-
905- CHECK (args[0 ]->IsString ());
906- Local<String> task_name = args[0 ].As <String>();
907- String::Value task_name_value (task_name);
908- StringView task_name_view (*task_name_value, task_name_value.length ());
909-
910- CHECK (args[1 ]->IsNumber ());
911- int64_t task_id = args[1 ]->IntegerValue (env->context ()).FromJust ();
912- void * task = GetAsyncTask (task_id);
913-
914- CHECK (args[2 ]->IsBoolean ());
915- bool recurring = args[2 ]->BooleanValue (env->context ()).FromJust ();
916-
917- env->inspector_agent ()->AsyncTaskScheduled (task_name_view, task, recurring);
918- }
919-
920- static void RegisterAsyncHookWrapper (const FunctionCallbackInfo<Value>& args) {
921- Environment* env = Environment::GetCurrent (args);
922-
923- CHECK (args[0 ]->IsFunction ());
924- v8::Local<v8::Function> enable_function = args[0 ].As <Function>();
925- CHECK (args[1 ]->IsFunction ());
926- v8::Local<v8::Function> disable_function = args[1 ].As <Function>();
927- env->inspector_agent ()->RegisterAsyncHook (env->isolate (),
928- enable_function, disable_function);
929- }
930-
931- static void IsEnabled (const FunctionCallbackInfo<Value>& args) {
932- Environment* env = Environment::GetCurrent (args);
933- args.GetReturnValue ().Set (env->inspector_agent ()->enabled ());
934- }
935-
936- // static
937- void Agent::InitInspector (Local<Object> target, Local<Value> unused,
938- Local<Context> context, void * priv) {
939- Environment* env = Environment::GetCurrent (context);
940- {
941- auto obj = Object::New (env->isolate ());
942- auto null = Null (env->isolate ());
943- CHECK (obj->SetPrototype (context, null).FromJust ());
944- env->set_inspector_console_api_object (obj);
945- }
946-
947- Agent* agent = env->inspector_agent ();
948- env->SetMethod (target, " consoleCall" , InspectorConsoleCall);
949- env->SetMethod (target, " addCommandLineAPI" , AddCommandLineAPI);
950- if (agent->debug_options_ .wait_for_connect ())
951- env->SetMethod (target, " callAndPauseOnStart" , CallAndPauseOnStart);
952- env->SetMethod (target, " open" , Open);
953- env->SetMethod (target, " url" , Url);
954-
955- env->SetMethod (target, " asyncTaskScheduled" , AsyncTaskScheduledWrapper);
956- env->SetMethod (target, " asyncTaskCanceled" ,
957- InvokeAsyncTaskFnWithId<&Agent::AsyncTaskCanceled>);
958- env->SetMethod (target, " asyncTaskStarted" ,
959- InvokeAsyncTaskFnWithId<&Agent::AsyncTaskStarted>);
960- env->SetMethod (target, " asyncTaskFinished" ,
961- InvokeAsyncTaskFnWithId<&Agent::AsyncTaskFinished>);
962-
963- env->SetMethod (target, " registerAsyncHook" , RegisterAsyncHookWrapper);
964- env->SetMethod (target, " isEnabled" , IsEnabled);
965-
966- auto conn_str = FIXED_ONE_BYTE_STRING (env->isolate (), " Connection" );
967- Local<FunctionTemplate> tmpl =
968- env->NewFunctionTemplate (JSBindingsConnection::New);
969- tmpl->InstanceTemplate ()->SetInternalFieldCount (1 );
970- tmpl->SetClassName (conn_str);
971- AsyncWrap::AddWrapMethods (env, tmpl);
972- env->SetProtoMethod (tmpl, " dispatch" , JSBindingsConnection::Dispatch);
973- env->SetProtoMethod (tmpl, " disconnect" , JSBindingsConnection::Disconnect);
974- target->Set (env->context (), conn_str, tmpl->GetFunction ()).ToChecked ();
975- }
976-
977651void Agent::RequestIoThreadStart () {
978652 // We need to attempt to interrupt V8 flow (in case Node is running
979653 // continuous JS code) and to wake up libuv thread (in case Node is waiting
@@ -993,8 +667,10 @@ void Agent::ContextCreated(Local<Context> context) {
993667 client_->contextCreated (context, name.str ());
994668}
995669
670+ bool Agent::IsWaitingForConnect () {
671+ return debug_options_.wait_for_connect ();
672+ }
673+
996674} // namespace inspector
997675} // namespace node
998676
999- NODE_MODULE_CONTEXT_AWARE_BUILTIN (inspector,
1000- node::inspector::Agent::InitInspector);
0 commit comments