Chromium Code Reviews| Index: runtime/vm/service.cc |
| diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc |
| index cbb169957da257ab2f08d355e08d29c1b4c9282c..0384b8aedc312bf411b8f74b031b5924b5b40824 100644 |
| --- a/runtime/vm/service.cc |
| +++ b/runtime/vm/service.cc |
| @@ -27,6 +27,7 @@ |
| #include "vm/port.h" |
| #include "vm/profiler_service.h" |
| #include "vm/reusable_handles.h" |
| +#include "vm/service_isolate.h" |
| #include "vm/stack_frame.h" |
| #include "vm/symbols.h" |
| #include "vm/unicode.h" |
| @@ -34,953 +35,647 @@ |
| namespace dart { |
| -DEFINE_FLAG(bool, trace_service, false, "Trace VM service requests."); |
| -DEFINE_FLAG(bool, trace_service_pause_events, false, |
| - "Trace VM service isolate pause events."); |
| +DECLARE_FLAG(bool, trace_service); |
| +DECLARE_FLAG(bool, trace_service_pause_events); |
| DECLARE_FLAG(bool, enable_type_checks); |
| DECLARE_FLAG(bool, enable_asserts); |
| -struct ResourcesEntry { |
| - const char* path_; |
| - const char* resource_; |
| - int length_; |
| -}; |
| - |
| -extern ResourcesEntry __service_resources_[]; |
| +EmbedderServiceHandler* Service::isolate_service_handler_head_ = NULL; |
| +EmbedderServiceHandler* Service::root_service_handler_head_ = NULL; |
|
turnidge
2015/02/12 18:08:57
Add a TODO to combine isolate and root service han
Cutch
2015/02/12 18:18:47
Done.
|
| +uint32_t Service::event_mask_ = 0; |
| +struct ServiceMethodDescriptor; |
| +ServiceMethodDescriptor* FindMethod(const char* method_name); |
| -class Resources { |
| - public: |
| - static const int kNoSuchInstance = -1; |
| - static int ResourceLookup(const char* path, const char** resource) { |
| - ResourcesEntry* table = ResourceTable(); |
| - for (int i = 0; table[i].path_ != NULL; i++) { |
| - const ResourcesEntry& entry = table[i]; |
| - if (strcmp(path, entry.path_) == 0) { |
| - *resource = entry.resource_; |
| - ASSERT(entry.length_ > 0); |
| - return entry.length_; |
| - } |
| - } |
| - return kNoSuchInstance; |
| - } |
| +static uint8_t* allocator(uint8_t* ptr, intptr_t old_size, intptr_t new_size) { |
| + void* new_ptr = realloc(reinterpret_cast<void*>(ptr), new_size); |
| + return reinterpret_cast<uint8_t*>(new_ptr); |
| +} |
| - static const char* Path(int idx) { |
| - ASSERT(idx >= 0); |
| - ResourcesEntry* entry = At(idx); |
| - if (entry == NULL) { |
| - return NULL; |
| +static void PrintRequest(const JSONObject& obj, JSONStream* js) { |
| + JSONObject jsobj(&obj, "request"); |
| + jsobj.AddProperty("method", js->method()); |
| + { |
| + JSONArray jsarr(&jsobj, "param_keys"); |
| + for (intptr_t i = 0; i < js->num_params(); i++) { |
| + jsarr.AddValue(js->GetParamKey(i)); |
| } |
| - ASSERT(entry->path_ != NULL); |
| - return entry->path_; |
| } |
| - |
| - static int Length(int idx) { |
| - ASSERT(idx >= 0); |
| - ResourcesEntry* entry = At(idx); |
| - if (entry == NULL) { |
| - return kNoSuchInstance; |
| + { |
| + JSONArray jsarr(&jsobj, "param_values"); |
| + for (intptr_t i = 0; i < js->num_params(); i++) { |
| + jsarr.AddValue(js->GetParamValue(i)); |
| } |
| - ASSERT(entry->path_ != NULL); |
| - return entry->length_; |
| } |
| +} |
| - static const uint8_t* Resource(int idx) { |
| - ASSERT(idx >= 0); |
| - ResourcesEntry* entry = At(idx); |
| - if (entry == NULL) { |
| - return NULL; |
| - } |
| - return reinterpret_cast<const uint8_t*>(entry->resource_); |
| - } |
| - private: |
| - static ResourcesEntry* At(int idx) { |
| - ASSERT(idx >= 0); |
| - ResourcesEntry* table = ResourceTable(); |
| - for (int i = 0; table[i].path_ != NULL; i++) { |
| - if (idx == i) { |
| - return &table[i]; |
| - } |
| - } |
| - return NULL; |
| - } |
| +static void PrintError(JSONStream* js, |
| + const char* format, ...) { |
| + Isolate* isolate = Isolate::Current(); |
| - static ResourcesEntry* ResourceTable() { |
| - return &__service_resources_[0]; |
| - } |
| + va_list args; |
| + va_start(args, format); |
| + intptr_t len = OS::VSNPrint(NULL, 0, format, args); |
| + va_end(args); |
| - DISALLOW_ALLOCATION(); |
| - DISALLOW_IMPLICIT_CONSTRUCTORS(Resources); |
| -}; |
| + char* buffer = isolate->current_zone()->Alloc<char>(len + 1); |
| + va_list args2; |
| + va_start(args2, format); |
| + OS::VSNPrint(buffer, (len + 1), format, args2); |
| + va_end(args2); |
| + JSONObject jsobj(js); |
| + jsobj.AddProperty("type", "Error"); |
| + jsobj.AddProperty("message", buffer); |
| + PrintRequest(jsobj, js); |
| +} |
| -class EmbedderServiceHandler { |
| - public: |
| - explicit EmbedderServiceHandler(const char* name) : name_(NULL), |
| - callback_(NULL), |
| - user_data_(NULL), |
| - next_(NULL) { |
| - ASSERT(name != NULL); |
| - name_ = strdup(name); |
| - } |
| - ~EmbedderServiceHandler() { |
| - free(name_); |
| - } |
| +static void PrintMissingParamError(JSONStream* js, |
| + const char* param) { |
| + PrintError(js, "%s expects the '%s' parameter", |
| + js->method(), param); |
| +} |
| - const char* name() const { return name_; } |
| - Dart_ServiceRequestCallback callback() const { return callback_; } |
| - void set_callback(Dart_ServiceRequestCallback callback) { |
| - callback_ = callback; |
| - } |
| +static void PrintInvalidParamError(JSONStream* js, |
| + const char* param) { |
| + PrintError(js, "%s: invalid '%s' parameter: %s", |
| + js->method(), param, js->LookupParam(param)); |
| +} |
| - void* user_data() const { return user_data_; } |
| - void set_user_data(void* user_data) { |
| - user_data_ = user_data; |
| - } |
| - EmbedderServiceHandler* next() const { return next_; } |
| - void set_next(EmbedderServiceHandler* next) { |
| - next_ = next; |
| - } |
| +static void PrintUnrecognizedMethodError(JSONStream* js) { |
| + PrintError(js, "unrecognized method: %s", js->method()); |
| +} |
| - private: |
| - char* name_; |
| - Dart_ServiceRequestCallback callback_; |
| - void* user_data_; |
| - EmbedderServiceHandler* next_; |
| -}; |
| +static void PrintErrorWithKind(JSONStream* js, |
| + const char* kind, |
| + const char* format, ...) { |
| + Isolate* isolate = Isolate::Current(); |
| -class LibraryCoverageFilter : public CoverageFilter { |
| - public: |
| - explicit LibraryCoverageFilter(const Library& lib) : lib_(lib) {} |
| - bool ShouldOutputCoverageFor(const Library& lib, |
| - const Script& script, |
| - const Class& cls, |
| - const Function& func) const { |
| - return lib.raw() == lib_.raw(); |
| - } |
| - private: |
| - const Library& lib_; |
| -}; |
| + va_list args; |
| + va_start(args, format); |
| + intptr_t len = OS::VSNPrint(NULL, 0, format, args); |
| + va_end(args); |
| + char* buffer = isolate->current_zone()->Alloc<char>(len + 1); |
| + va_list args2; |
| + va_start(args2, format); |
| + OS::VSNPrint(buffer, (len + 1), format, args2); |
| + va_end(args2); |
| -class ScriptCoverageFilter : public CoverageFilter { |
| - public: |
| - explicit ScriptCoverageFilter(const Script& script) |
| - : script_(script) {} |
| - bool ShouldOutputCoverageFor(const Library& lib, |
| - const Script& script, |
| - const Class& cls, |
| - const Function& func) const { |
| - return script.raw() == script_.raw(); |
| - } |
| - private: |
| - const Script& script_; |
| -}; |
| + JSONObject jsobj(js); |
| + jsobj.AddProperty("type", "Error"); |
| + jsobj.AddProperty("id", ""); |
| + jsobj.AddProperty("kind", kind); |
| + jsobj.AddProperty("message", buffer); |
| + PrintRequest(jsobj, js); |
| +} |
| -class ClassCoverageFilter : public CoverageFilter { |
| - public: |
| - explicit ClassCoverageFilter(const Class& cls) : cls_(cls) {} |
| - bool ShouldOutputCoverageFor(const Library& lib, |
| - const Script& script, |
| - const Class& cls, |
| - const Function& func) const { |
| - return cls.raw() == cls_.raw(); |
| +static bool GetIntegerId(const char* s, intptr_t* id, int base = 10) { |
| + if ((s == NULL) || (*s == '\0')) { |
| + // Empty string. |
| + return false; |
| } |
| - private: |
| - const Class& cls_; |
| -}; |
| - |
| - |
| -class FunctionCoverageFilter : public CoverageFilter { |
| - public: |
| - explicit FunctionCoverageFilter(const Function& func) : func_(func) {} |
| - bool ShouldOutputCoverageFor(const Library& lib, |
| - const Script& script, |
| - const Class& cls, |
| - const Function& func) const { |
| - return func.raw() == func_.raw(); |
| + if (id == NULL) { |
| + // No id pointer. |
| + return false; |
| } |
| - private: |
| - const Function& func_; |
| -}; |
| + intptr_t r = 0; |
| + char* end_ptr = NULL; |
| + r = strtol(s, &end_ptr, base); |
| + if (end_ptr == s) { |
| + // String was not advanced at all, cannot be valid. |
| + return false; |
| + } |
| + *id = r; |
| + return true; |
| +} |
| -static uint8_t* allocator(uint8_t* ptr, intptr_t old_size, intptr_t new_size) { |
| - void* new_ptr = realloc(reinterpret_cast<void*>(ptr), new_size); |
| - return reinterpret_cast<uint8_t*>(new_ptr); |
| +static bool GetUnsignedIntegerId(const char* s, uintptr_t* id, int base = 10) { |
| + if ((s == NULL) || (*s == '\0')) { |
| + // Empty string. |
| + return false; |
| + } |
| + if (id == NULL) { |
| + // No id pointer. |
| + return false; |
| + } |
| + uintptr_t r = 0; |
| + char* end_ptr = NULL; |
| + r = strtoul(s, &end_ptr, base); |
| + if (end_ptr == s) { |
| + // String was not advanced at all, cannot be valid. |
| + return false; |
| + } |
| + *id = r; |
| + return true; |
| } |
| -static void SendIsolateServiceMessage(Dart_NativeArguments args) { |
| - NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); |
| - Isolate* isolate = arguments->isolate(); |
| - StackZone zone(isolate); |
| - HANDLESCOPE(isolate); |
| - GET_NON_NULL_NATIVE_ARGUMENT(SendPort, sp, arguments->NativeArgAt(0)); |
| - GET_NON_NULL_NATIVE_ARGUMENT(Array, message, arguments->NativeArgAt(1)); |
| - |
| - // Set the type of the OOB message. |
| - message.SetAt(0, Smi::Handle(isolate, Smi::New(Message::kServiceOOBMsg))); |
| +static bool GetInteger64Id(const char* s, int64_t* id, int base = 10) { |
| + if ((s == NULL) || (*s == '\0')) { |
| + // Empty string. |
| + return false; |
| + } |
| + if (id == NULL) { |
| + // No id pointer. |
| + return false; |
| + } |
| + int64_t r = 0; |
| + char* end_ptr = NULL; |
| + r = strtoll(s, &end_ptr, base); |
| + if (end_ptr == s) { |
| + // String was not advanced at all, cannot be valid. |
| + return false; |
| + } |
| + *id = r; |
| + return true; |
| +} |
| - // Serialize message. |
| - uint8_t* data = NULL; |
| - MessageWriter writer(&data, &allocator, false); |
| - writer.WriteMessage(message); |
| - // TODO(turnidge): Throw an exception when the return value is false? |
| - PortMap::PostMessage(new Message(sp.Id(), data, writer.BytesWritten(), |
| - Message::kOOBPriority)); |
| +// Scans the string until the '-' character. Returns pointer to string |
| +// at '-' character. Returns NULL if not found. |
| +static const char* ScanUntilDash(const char* s) { |
| + if ((s == NULL) || (*s == '\0')) { |
| + // Empty string. |
| + return NULL; |
| + } |
| + while (*s != '\0') { |
| + if (*s == '-') { |
| + return s; |
| + } |
| + s++; |
| + } |
| + return NULL; |
| } |
| -static void SendRootServiceMessage(Dart_NativeArguments args) { |
| - NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); |
| - Isolate* isolate = arguments->isolate(); |
| - StackZone zone(isolate); |
| - HANDLESCOPE(isolate); |
| - GET_NON_NULL_NATIVE_ARGUMENT(Instance, message, arguments->NativeArgAt(0)); |
| - Service::HandleRootMessage(message); |
| +static bool GetCodeId(const char* s, int64_t* timestamp, uword* address) { |
| + if ((s == NULL) || (*s == '\0')) { |
| + // Empty string. |
| + return false; |
| + } |
| + if ((timestamp == NULL) || (address == NULL)) { |
| + // Bad arguments. |
| + return false; |
| + } |
| + // Extract the timestamp. |
| + if (!GetInteger64Id(s, timestamp, 16) || (*timestamp < 0)) { |
| + return false; |
| + } |
| + s = ScanUntilDash(s); |
| + if (s == NULL) { |
| + return false; |
| + } |
| + // Skip the dash. |
| + s++; |
| + // Extract the PC. |
| + if (!GetUnsignedIntegerId(s, address, 16)) { |
| + return false; |
| + } |
| + return true; |
| } |
| -class ScopeStopwatch : public ValueObject { |
| +class MethodParameter { |
|
turnidge
2015/02/12 18:08:57
Consider adding standalone unit tests for BoolPara
Cutch
2015/02/12 18:18:47
I've added a TODO.
|
| public: |
| - explicit ScopeStopwatch(const char* name) : name_(name) { |
| - start_ = OS::GetCurrentTimeMicros(); |
| + MethodParameter(const char* name, bool required) |
| + : name_(name), required_(required) { |
| + } |
| + |
| + virtual ~MethodParameter() { } |
| + |
| + virtual bool Validate(const char* value) const { |
| + return true; |
| } |
| - int64_t GetElapsed() const { |
| - int64_t end = OS::GetCurrentTimeMicros(); |
| - ASSERT(end >= start_); |
| - return end - start_; |
| + const char* name() const { |
| + return name_; |
| } |
| - ~ScopeStopwatch() { |
| - int64_t elapsed = GetElapsed(); |
| - OS::Print("[%" Pd "] %s took %" Pd64 " micros.\n", |
| - OS::ProcessId(), name_, elapsed); |
| + bool required() const { |
| + return required_; |
| } |
| private: |
| const char* name_; |
| - int64_t start_; |
| + bool required_; |
| }; |
| -bool Service::IsRunning() { |
| - MonitorLocker ml(monitor_); |
| - return (service_port_ != ILLEGAL_PORT) && (service_isolate_ != NULL); |
| -} |
| +class BoolParameter : public MethodParameter { |
| + public: |
| + BoolParameter(const char* name, bool required) |
| + : MethodParameter(name, required) { |
| + } |
| + virtual bool Validate(const char* value) const { |
| + if (value == NULL) { |
| + return false; |
| + } |
| + return (strcmp("true", value) == 0) || (strcmp("false", value) == 0); |
| + } |
| -void Service::SetServicePort(Dart_Port port) { |
| - MonitorLocker ml(monitor_); |
| - service_port_ = port; |
| -} |
| + static bool Interpret(const char* value) { |
| + return strcmp("true", value) == 0; |
| + } |
| +}; |
| -void Service::SetServiceIsolate(Isolate* isolate) { |
| - MonitorLocker ml(monitor_); |
| - service_isolate_ = isolate; |
| - if (service_isolate_ != NULL) { |
| - service_isolate_->is_service_isolate_ = true; |
| +class IdParameter : public MethodParameter { |
| + public: |
| + IdParameter(const char* name, bool required) |
| + : MethodParameter(name, required) { |
| } |
| -} |
| + virtual bool Validate(const char* value) const { |
| + return (value != NULL); |
| + } |
| +}; |
| -bool Service::HasServiceIsolate() { |
| - MonitorLocker ml(monitor_); |
| - return service_isolate_ != NULL; |
| -} |
| +#define ISOLATE_PARAMETER new IdParameter("isolateId", true) |
| -bool Service::IsServiceIsolate(Isolate* isolate) { |
| - MonitorLocker ml(monitor_); |
| - return isolate == service_isolate_; |
| -} |
| -Dart_Port Service::WaitForLoadPort() { |
| - MonitorLocker ml(monitor_); |
| +class EnumParameter : public MethodParameter { |
| + public: |
| + EnumParameter(const char* name, bool required, const char** enums) |
| + : MethodParameter(name, required), |
| + enums_(enums) { |
| + } |
| - while (initializing_ && (load_port_ == ILLEGAL_PORT)) { |
| - ml.Wait(); |
| + virtual bool Validate(const char* value) const { |
| + if (value == NULL) { |
| + return true; |
| + } |
| + for (intptr_t i = 0; enums_[i] != NULL; i++) { |
| + if (strcmp(value, enums_[i]) == 0) { |
| + return true; |
| + } |
| + } |
| + return false; |
| } |
| - return load_port_; |
| -} |
| + private: |
| + const char** enums_; |
| +}; |
| -Dart_Port Service::LoadPort() { |
| - MonitorLocker ml(monitor_); |
| - return load_port_; |
| +// If the key is not found, this function returns the last element in the |
| +// values array. This can be used to encode the default value. |
| +template<typename T> |
| +T EnumMapper(const char* value, const char** enums, T* values) { |
| + ASSERT(value != NULL); |
| + intptr_t i = 0; |
| + for (i = 0; enums[i] != NULL; i++) { |
| + if (strcmp(value, enums[i]) == 0) { |
| + return values[i]; |
| + } |
| + } |
| + // Default value. |
| + return values[i]; |
| } |
| -void Service::SetLoadPort(Dart_Port port) { |
| - MonitorLocker ml(monitor_); |
| - load_port_ = port; |
| -} |
| +typedef bool (*ServiceMethodEntry)(Isolate* isolate, JSONStream* js); |
| -void Service::SetEventMask(uint32_t mask) { |
| - event_mask_ = mask; |
| -} |
| +struct ServiceMethodDescriptor { |
| + const char* name; |
| + const ServiceMethodEntry entry; |
| + const MethodParameter* const * parameters; |
| +}; |
| -static void SetEventMask(Dart_NativeArguments args) { |
| - NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); |
| - Isolate* isolate = arguments->isolate(); |
| - StackZone zone(isolate); |
| - HANDLESCOPE(isolate); |
| - GET_NON_NULL_NATIVE_ARGUMENT(Integer, mask, arguments->NativeArgAt(0)); |
| - Service::SetEventMask(mask.AsTruncatedUint32Value()); |
| +static bool ValidateParameters(const MethodParameter* const* parameters, |
| + JSONStream* js) { |
| + if (parameters == NULL) { |
| + return true; |
| + } |
| + for (intptr_t i = 0; parameters[i] != NULL; i++) { |
| + const MethodParameter* parameter = parameters[i]; |
| + const char* name = parameter->name(); |
| + const bool required = parameter->required(); |
| + const char* value = js->LookupParam(name); |
| + const bool has_parameter = (value != NULL); |
| + if (required && !has_parameter) { |
| + PrintMissingParamError(js, name); |
| + return false; |
| + } |
| + if (!parameter->Validate(value)) { |
| + PrintInvalidParamError(js, name); |
| + return false; |
| + } |
| + } |
|
turnidge
2015/02/12 18:08:57
We don't complain about unexpected extra parameter
|
| + return true; |
| } |
| -// These must be kept in sync with service/constants.dart |
| -#define VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID 1 |
| -#define VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID 2 |
| +void Service::InvokeMethod(Isolate* isolate, const Array& msg) { |
| + ASSERT(isolate != NULL); |
| + ASSERT(!msg.IsNull()); |
| + ASSERT(msg.Length() == 5); |
| + |
| + { |
| + StackZone zone(isolate); |
| + HANDLESCOPE(isolate); |
| + |
| + Instance& reply_port = Instance::Handle(isolate); |
| + String& method_name = String::Handle(isolate); |
| + Array& param_keys = Array::Handle(isolate); |
| + Array& param_values = Array::Handle(isolate); |
| + reply_port ^= msg.At(1); |
| + method_name ^= msg.At(2); |
| + param_keys ^= msg.At(3); |
| + param_values ^= msg.At(4); |
| + ASSERT(!method_name.IsNull()); |
| + ASSERT(!param_keys.IsNull()); |
| + ASSERT(!param_values.IsNull()); |
| + ASSERT(param_keys.Length() == param_values.Length()); |
| -static RawArray* MakeServiceControlMessage(Dart_Port port_id, intptr_t code, |
| - const String& name) { |
| - const Array& list = Array::Handle(Array::New(4)); |
| - ASSERT(!list.IsNull()); |
| - const Integer& code_int = Integer::Handle(Integer::New(code)); |
| - const Integer& port_int = Integer::Handle(Integer::New(port_id)); |
| - const SendPort& send_port = SendPort::Handle(SendPort::New(port_id)); |
| - list.SetAt(0, code_int); |
| - list.SetAt(1, port_int); |
| - list.SetAt(2, send_port); |
| - list.SetAt(3, name); |
| - return list.raw(); |
| -} |
| + if (!reply_port.IsSendPort()) { |
| + FATAL("SendPort expected."); |
| + } |
| + JSONStream js; |
| + js.Setup(zone.GetZone(), SendPort::Cast(reply_port).Id(), |
| + method_name, param_keys, param_values); |
| -class RegisterRunningIsolatesVisitor : public IsolateVisitor { |
| - public: |
| - explicit RegisterRunningIsolatesVisitor(Isolate* service_isolate) |
| - : IsolateVisitor(), |
| - register_function_(Function::Handle(service_isolate)), |
| - service_isolate_(service_isolate) { |
| - ASSERT(Service::IsServiceIsolate(Isolate::Current())); |
| - // Get library. |
| - const String& library_url = Symbols::DartVMService(); |
| - ASSERT(!library_url.IsNull()); |
| - const Library& library = |
| - Library::Handle(Library::LookupLibrary(library_url)); |
| - ASSERT(!library.IsNull()); |
| - // Get function. |
| - const String& function_name = |
| - String::Handle(String::New("_registerIsolate")); |
| - ASSERT(!function_name.IsNull()); |
| - register_function_ = library.LookupFunctionAllowPrivate(function_name); |
| - ASSERT(!register_function_.IsNull()); |
| - } |
| - |
| - virtual void VisitIsolate(Isolate* isolate) { |
| - ASSERT(Service::IsServiceIsolate(Isolate::Current())); |
| - if (Service::IsServiceIsolate(isolate) || |
| - (isolate == Dart::vm_isolate())) { |
| - // We do not register the service or vm isolate. |
| + const char* c_method_name = method_name.ToCString(); |
| + |
| + ServiceMethodDescriptor* method = FindMethod(c_method_name); |
| + if (method != NULL) { |
| + if (!ValidateParameters(method->parameters, &js)) { |
| + js.PostReply(); |
| + return; |
| + } |
| + if (method->entry(isolate, &js)) { |
| + js.PostReply(); |
| + } |
| return; |
| } |
| - // Setup arguments for call. |
| - Dart_Port port_id = isolate->main_port(); |
| - const Integer& port_int = Integer::Handle(Integer::New(port_id)); |
| - ASSERT(!port_int.IsNull()); |
| - const SendPort& send_port = SendPort::Handle(SendPort::New(port_id)); |
| - const String& name = String::Handle(String::New(isolate->name())); |
| - ASSERT(!name.IsNull()); |
| - const Array& args = Array::Handle(Array::New(3)); |
| - ASSERT(!args.IsNull()); |
| - args.SetAt(0, port_int); |
| - args.SetAt(1, send_port); |
| - args.SetAt(2, name); |
| - Object& r = Object::Handle(service_isolate_); |
| - r = DartEntry::InvokeFunction(register_function_, args); |
| - if (FLAG_trace_service) { |
| - OS::Print("vm-service: Isolate %s %" Pd64 " registered.\n", |
| - name.ToCString(), |
| - port_id); |
| - } |
| - ASSERT(!r.IsError()); |
| - } |
| - private: |
| - Function& register_function_; |
| - Isolate* service_isolate_; |
| -}; |
| + EmbedderServiceHandler* handler = FindIsolateEmbedderHandler(c_method_name); |
| + if (handler == NULL) { |
| + handler = FindRootEmbedderHandler(c_method_name); |
| + } |
| + if (handler != NULL) { |
| + EmbedderHandleMessage(handler, &js); |
| + js.PostReply(); |
| + return; |
| + } |
| -static Dart_Port ExtractPort(Isolate* isolate, Dart_Handle receivePort) { |
| - const ReceivePort& rp = Api::UnwrapReceivePortHandle(isolate, receivePort); |
| - if (rp.IsNull()) { |
| - return ILLEGAL_PORT; |
| + PrintUnrecognizedMethodError(&js); |
| + js.PostReply(); |
| + return; |
| } |
| - return rp.Id(); |
| } |
| -static void OnStart(Dart_NativeArguments args) { |
| - NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); |
| - Isolate* isolate = arguments->isolate(); |
| - StackZone zone(isolate); |
| - HANDLESCOPE(isolate); |
| - { |
| - if (FLAG_trace_service) { |
| - OS::Print("vm-service: Booting dart:vmservice library.\n"); |
| - } |
| - // Boot the dart:vmservice library. |
| - Dart_EnterScope(); |
| - Dart_Handle url_str = |
| - Dart_NewStringFromCString(Symbols::Name(Symbols::kDartVMServiceId)); |
| - Dart_Handle library = Dart_LookupLibrary(url_str); |
| - ASSERT(Dart_IsLibrary(library)); |
| - Dart_Handle result = |
| - Dart_Invoke(library, Dart_NewStringFromCString("boot"), 0, NULL); |
| - ASSERT(!Dart_IsError(result)); |
| - Dart_Port port = ExtractPort(isolate, result); |
| - ASSERT(port != ILLEGAL_PORT); |
| - Service::SetServicePort(port); |
| - Dart_ExitScope(); |
| - } |
| - |
| - { |
| - if (FLAG_trace_service) { |
| - OS::Print("vm-service: Registering running isolates.\n"); |
| - } |
| - // Register running isolates with service. |
| - RegisterRunningIsolatesVisitor register_isolates(isolate); |
| - Isolate::VisitIsolates(®ister_isolates); |
| - } |
| +void Service::HandleRootMessage(const Array& msg_instance) { |
| + Isolate* isolate = Isolate::Current(); |
| + InvokeMethod(isolate, msg_instance); |
| } |
| -struct VmServiceNativeEntry { |
| - const char* name; |
| - int num_arguments; |
| - Dart_NativeFunction function; |
| -}; |
| +void Service::HandleIsolateMessage(Isolate* isolate, const Array& msg) { |
| + ASSERT(isolate != NULL); |
| + InvokeMethod(isolate, msg); |
| +} |
| -static VmServiceNativeEntry _VmServiceNativeEntries[] = { |
| - {"VMService_SendIsolateServiceMessage", 2, SendIsolateServiceMessage}, |
| - {"VMService_SendRootServiceMessage", 1, SendRootServiceMessage}, |
| - {"VMService_SetEventMask", 1, SetEventMask}, |
| - {"VMService_OnStart", 0, OnStart }, |
| -}; |
| +bool Service::EventMaskHas(uint32_t mask) { |
| + return (event_mask_ & mask) != 0; |
| +} |
| -static Dart_NativeFunction VmServiceNativeResolver(Dart_Handle name, |
| - int num_arguments, |
| - bool* auto_setup_scope) { |
| - const Object& obj = Object::Handle(Api::UnwrapHandle(name)); |
| - if (!obj.IsString()) { |
| - return NULL; |
| - } |
| - const char* function_name = obj.ToCString(); |
| - ASSERT(function_name != NULL); |
| - ASSERT(auto_setup_scope != NULL); |
| - *auto_setup_scope = true; |
| - intptr_t n = |
| - sizeof(_VmServiceNativeEntries) / sizeof(_VmServiceNativeEntries[0]); |
| - for (intptr_t i = 0; i < n; i++) { |
| - VmServiceNativeEntry entry = _VmServiceNativeEntries[i]; |
| - if ((strcmp(function_name, entry.name) == 0) && |
| - (num_arguments == entry.num_arguments)) { |
| - return entry.function; |
| - } |
| - } |
| - return NULL; |
| +bool Service::NeedsDebuggerEvents() { |
| + return ServiceIsolate::IsRunning() && EventMaskHas(kEventFamilyDebugMask); |
| } |
| -const char* Service::kIsolateName = "vm-service"; |
| -EmbedderServiceHandler* Service::isolate_service_handler_head_ = NULL; |
| -EmbedderServiceHandler* Service::root_service_handler_head_ = NULL; |
| -Isolate* Service::service_isolate_ = NULL; |
| -Dart_Port Service::service_port_ = ILLEGAL_PORT; |
| -Dart_Port Service::load_port_ = ILLEGAL_PORT; |
| -Dart_IsolateCreateCallback Service::create_callback_ = NULL; |
| -Monitor* Service::monitor_ = NULL; |
| -bool Service::initializing_ = true; |
| -uint32_t Service::event_mask_ = 0; |
| - |
| -bool Service::IsServiceIsolateName(const char* name) { |
| - ASSERT(name != NULL); |
| - return strcmp(name, kIsolateName) == 0; |
| +bool Service::NeedsGCEvents() { |
| + return ServiceIsolate::IsRunning() && EventMaskHas(kEventFamilyGCMask); |
| } |
| -bool Service::SendIsolateStartupMessage() { |
| - if (!IsRunning()) { |
| - return false; |
| - } |
| - Isolate* isolate = Isolate::Current(); |
| - if (IsServiceIsolate(isolate)) { |
| - return false; |
| - } |
| - ASSERT(isolate != NULL); |
| - HANDLESCOPE(isolate); |
| - const String& name = String::Handle(String::New(isolate->name())); |
| - ASSERT(!name.IsNull()); |
| - const Array& list = Array::Handle( |
| - MakeServiceControlMessage(Dart_GetMainPortId(), |
| - VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID, |
| - name)); |
| - ASSERT(!list.IsNull()); |
| - uint8_t* data = NULL; |
| - MessageWriter writer(&data, &allocator, false); |
| - writer.WriteMessage(list); |
| - intptr_t len = writer.BytesWritten(); |
| - if (FLAG_trace_service) { |
| - OS::Print("vm-service: Isolate %s %" Pd64 " registered.\n", |
| - name.ToCString(), |
| - Dart_GetMainPortId()); |
| - } |
| - return PortMap::PostMessage( |
| - new Message(service_port_, data, len, Message::kNormalPriority)); |
| +void Service::SetEventMask(uint32_t mask) { |
| + event_mask_ = mask; |
| } |
| -bool Service::SendIsolateShutdownMessage() { |
| - if (!IsRunning()) { |
| - return false; |
| +void Service::SendEvent(intptr_t eventId, const Object& eventMessage) { |
| + if (!ServiceIsolate::IsRunning()) { |
| + return; |
| } |
| Isolate* isolate = Isolate::Current(); |
| - if (IsServiceIsolate(isolate)) { |
| - return false; |
| - } |
| ASSERT(isolate != NULL); |
| HANDLESCOPE(isolate); |
| - const String& name = String::Handle(String::New(isolate->name())); |
| - ASSERT(!name.IsNull()); |
| - const Array& list = Array::Handle( |
| - MakeServiceControlMessage(Dart_GetMainPortId(), |
| - VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID, |
| - name)); |
| + |
| + // Construct a list of the form [eventId, eventMessage]. |
| + const Array& list = Array::Handle(Array::New(2)); |
| ASSERT(!list.IsNull()); |
| + list.SetAt(0, Integer::Handle(Integer::New(eventId))); |
| + list.SetAt(1, eventMessage); |
| + |
| + // Push the event to port_. |
| uint8_t* data = NULL; |
| MessageWriter writer(&data, &allocator, false); |
| writer.WriteMessage(list); |
| intptr_t len = writer.BytesWritten(); |
| if (FLAG_trace_service) { |
| - OS::Print("vm-service: Isolate %s %" Pd64 " deregistered.\n", |
| - name.ToCString(), |
| - Dart_GetMainPortId()); |
| - } |
| - return PortMap::PostMessage( |
| - new Message(service_port_, data, len, Message::kNormalPriority)); |
| -} |
| - |
| - |
| -Dart_Handle Service::GetSource(const char* name) { |
| - ASSERT(name != NULL); |
| - int i = 0; |
| - while (true) { |
| - const char* path = Resources::Path(i); |
| - if (path == NULL) { |
| - break; |
| - } |
| - ASSERT(*path != '\0'); |
| - // Skip the '/'. |
| - path++; |
| - if (strcmp(name, path) == 0) { |
| - const uint8_t* str = Resources::Resource(i); |
| - intptr_t length = Resources::Length(i); |
| - return Dart_NewStringFromUTF8(str, length); |
| - } |
| - i++; |
| - } |
| - return Dart_Null(); |
| -} |
| - |
| - |
| -Dart_Handle Service::LibraryTagHandler(Dart_LibraryTag tag, |
| - Dart_Handle library, |
| - Dart_Handle url) { |
| - if (tag == Dart_kCanonicalizeUrl) { |
| - // url is already canonicalized. |
| - return url; |
| - } |
| - if (tag != Dart_kSourceTag) { |
| - FATAL("Service::LibraryTagHandler encountered an unexpected tag."); |
| - } |
| - ASSERT(tag == Dart_kSourceTag); |
| - const char* url_string = NULL; |
| - Dart_Handle result = Dart_StringToCString(url, &url_string); |
| - if (Dart_IsError(result)) { |
| - return result; |
| - } |
| - Dart_Handle source = GetSource(url_string); |
| - if (Dart_IsError(source)) { |
| - return source; |
| + OS::Print("vm-service: Pushing event of type %" Pd ", len %" Pd "\n", |
| + eventId, len); |
| } |
| - return Dart_LoadSource(library, url, source, 0, 0); |
| + // TODO(turnidge): For now we ignore failure to send an event. Revisit? |
| + PortMap::PostMessage( |
| + new Message(ServiceIsolate::Port(), data, len, Message::kNormalPriority)); |
| } |
| -void Service::MaybeInjectVMServiceLibrary(Isolate* isolate) { |
| - ASSERT(isolate != NULL); |
| - ASSERT(isolate->name() != NULL); |
| - if (!Service::IsServiceIsolateName(isolate->name())) { |
| - // Not service isolate. |
| - return; |
| +void Service::SendEvent(intptr_t eventId, |
| + const String& meta, |
| + const uint8_t* data, |
| + intptr_t size) { |
| + // Bitstream: [meta data size (big-endian 64 bit)] [meta data (UTF-8)] [data] |
| + const intptr_t meta_bytes = Utf8::Length(meta); |
| + const intptr_t total_bytes = sizeof(uint64_t) + meta_bytes + size; |
| + const TypedData& message = TypedData::Handle( |
| + TypedData::New(kTypedDataUint8ArrayCid, total_bytes)); |
| + intptr_t offset = 0; |
| + // TODO(koda): Rename these methods SetHostUint64, etc. |
| + message.SetUint64(0, Utils::HostToBigEndian64(meta_bytes)); |
| + offset += sizeof(uint64_t); |
| + { |
| + NoGCScope no_gc; |
| + meta.ToUTF8(static_cast<uint8_t*>(message.DataAddr(offset)), meta_bytes); |
| + offset += meta_bytes; |
| } |
| - if (HasServiceIsolate()) { |
| - // Service isolate already exists. |
| - return; |
| + // TODO(koda): It would be nice to avoid this copy (requires changes to |
| + // MessageWriter code). |
| + { |
| + NoGCScope no_gc; |
| + memmove(message.DataAddr(offset), data, size); |
| + offset += size; |
| } |
| - SetServiceIsolate(isolate); |
| - |
| - StackZone zone(isolate); |
| - HANDLESCOPE(isolate); |
| - |
| - // Register dart:vmservice library. |
| - const String& url_str = String::Handle(Symbols::DartVMService().raw()); |
| - const Library& library = Library::Handle(Library::New(url_str)); |
| - library.Register(); |
| - library.set_native_entry_resolver(VmServiceNativeResolver); |
| - |
| - // Temporarily install our library tag handler. |
| - isolate->set_library_tag_handler(LibraryTagHandler); |
| - |
| - // Get script source. |
| - const char* resource = NULL; |
| - const char* path = "/vmservice.dart"; |
| - intptr_t r = Resources::ResourceLookup(path, &resource); |
| - ASSERT(r != Resources::kNoSuchInstance); |
| - ASSERT(resource != NULL); |
| - const String& source_str = String::Handle( |
| - String::FromUTF8(reinterpret_cast<const uint8_t*>(resource), r)); |
| - ASSERT(!source_str.IsNull()); |
| - const Script& script = Script::Handle( |
| - isolate, Script::New(url_str, source_str, RawScript::kLibraryTag)); |
| - |
| - // Compile script. |
| - Dart_EnterScope(); // Need to enter scope for tag handler. |
| - library.SetLoadInProgress(); |
| - const Error& error = Error::Handle(isolate, |
| - Compiler::Compile(library, script)); |
| - ASSERT(error.IsNull()); |
| - Dart_Handle result = Dart_FinalizeLoading(false); |
| - ASSERT(!Dart_IsError(result)); |
| - Dart_ExitScope(); |
| - |
| - // Uninstall our library tag handler. |
| - isolate->set_library_tag_handler(NULL); |
| + ASSERT(offset == total_bytes); |
| + SendEvent(eventId, message); |
| } |
| -void Service::FinishedInitializing() { |
| - MonitorLocker ml(monitor_); |
| - initializing_ = false; |
| - ml.NotifyAll(); |
| +void Service::HandleGCEvent(GCEvent* event) { |
| + JSONStream js; |
| + event->PrintJSON(&js); |
| + const String& message = String::Handle(String::New(js.ToCString())); |
| + SendEvent(kEventFamilyGC, message); |
| } |
| -static void ShutdownIsolate(uword parameter) { |
| - Isolate* isolate = reinterpret_cast<Isolate*>(parameter); |
| - ASSERT(Service::IsServiceIsolate(isolate)); |
| - { |
| - // Print the error if there is one. This may execute dart code to |
| - // print the exception object, so we need to use a StartIsolateScope. |
| - StartIsolateScope start_scope(isolate); |
| - StackZone zone(isolate); |
| - HandleScope handle_scope(isolate); |
| - Error& error = Error::Handle(); |
| - error = isolate->object_store()->sticky_error(); |
| - if (!error.IsNull()) { |
| - OS::PrintErr("vm-service: Error: %s\n", error.ToErrorCString()); |
| - } |
| - Dart::RunShutdownCallback(); |
| - } |
| - { |
| - // Shut the isolate down. |
| - SwitchIsolateScope switch_scope(isolate); |
| - Dart::ShutdownIsolate(); |
| - } |
| - Service::SetServiceIsolate(NULL); |
| - Service::SetServicePort(ILLEGAL_PORT); |
| - if (FLAG_trace_service) { |
| - OS::Print("vm-service: Shutdown.\n"); |
| - } |
| +void Service::HandleDebuggerEvent(DebuggerEvent* event) { |
| + JSONStream js; |
| + event->PrintJSON(&js); |
| + const String& message = String::Handle(String::New(js.ToCString())); |
| + SendEvent(kEventFamilyDebug, message); |
| } |
| -class RunServiceTask : public ThreadPool::Task { |
| +class EmbedderServiceHandler { |
| public: |
| - virtual void Run() { |
| - ASSERT(Isolate::Current() == NULL); |
| - char* error = NULL; |
| - Isolate* isolate = NULL; |
| - |
| - Dart_IsolateCreateCallback create_callback = Service::create_callback(); |
| - // TODO(johnmccutchan): Support starting up service isolate without embedder |
| - // provided isolate creation callback. |
| - if (create_callback == NULL) { |
| - Service::FinishedInitializing(); |
| - return; |
| - } |
| - |
| - isolate = |
| - reinterpret_cast<Isolate*>(create_callback(Service::kIsolateName, |
| - NULL, |
| - NULL, |
| - NULL, |
| - &error)); |
| - if (isolate == NULL) { |
| - OS::PrintErr("vm-service: Isolate creation error: %s\n", error); |
| - Service::FinishedInitializing(); |
| - return; |
| - } |
| - |
| - Isolate::SetCurrent(NULL); |
| + explicit EmbedderServiceHandler(const char* name) : name_(NULL), |
| + callback_(NULL), |
| + user_data_(NULL), |
| + next_(NULL) { |
| + ASSERT(name != NULL); |
| + name_ = strdup(name); |
| + } |
| - RunMain(isolate); |
| + ~EmbedderServiceHandler() { |
| + free(name_); |
| + } |
| - Service::FinishedInitializing(); |
| + const char* name() const { return name_; } |
| - isolate->message_handler()->Run(Dart::thread_pool(), |
| - NULL, |
| - ShutdownIsolate, |
| - reinterpret_cast<uword>(isolate)); |
| + Dart_ServiceRequestCallback callback() const { return callback_; } |
| + void set_callback(Dart_ServiceRequestCallback callback) { |
| + callback_ = callback; |
| } |
| - protected: |
| - void RunMain(Isolate* isolate) { |
| - StartIsolateScope iso_scope(isolate); |
| - StackZone zone(isolate); |
| - HANDLESCOPE(isolate); |
| - // Invoke main which will return the loadScriptPort. |
| - const Library& root_library = |
| - Library::Handle(isolate, isolate->object_store()->root_library()); |
| - if (root_library.IsNull()) { |
| - if (FLAG_trace_service) { |
| - OS::Print("vm-service: Embedder did not install a script."); |
| - } |
| - // Service isolate is not supported by embedder. |
| - return; |
| - } |
| - ASSERT(!root_library.IsNull()); |
| - const String& entry_name = String::Handle(isolate, String::New("main")); |
| - ASSERT(!entry_name.IsNull()); |
| - const Function& entry = |
| - Function::Handle(isolate, |
| - root_library.LookupFunctionAllowPrivate(entry_name)); |
| - if (entry.IsNull()) { |
| - // Service isolate is not supported by embedder. |
| - if (FLAG_trace_service) { |
| - OS::Print("vm-service: Embedder did not provide a main function."); |
| - } |
| - return; |
| - } |
| - ASSERT(!entry.IsNull()); |
| - const Object& result = |
| - Object::Handle(isolate, |
| - DartEntry::InvokeFunction(entry, |
| - Object::empty_array())); |
| - ASSERT(!result.IsNull()); |
| - if (result.IsError()) { |
| - // Service isolate did not initialize properly. |
| - if (FLAG_trace_service) { |
| - const Error& error = Error::Cast(result); |
| - OS::Print("vm-service: Calling main resulted in an error: %s", |
| - error.ToErrorCString()); |
| - } |
| - return; |
| - } |
| - ASSERT(result.IsReceivePort()); |
| - const ReceivePort& rp = ReceivePort::Cast(result); |
| - Service::SetLoadPort(rp.Id()); |
| + void* user_data() const { return user_data_; } |
| + void set_user_data(void* user_data) { |
| + user_data_ = user_data; |
| } |
| -}; |
| - |
| -void Service::RunService() { |
| - ASSERT(monitor_ == NULL); |
| - monitor_ = new Monitor(); |
| - ASSERT(monitor_ != NULL); |
| - // Grab the isolate create callback here to avoid race conditions with tests |
| - // that change this after Dart_Initialize returns. |
| - create_callback_ = Isolate::CreateCallback(); |
| - Dart::thread_pool()->Run(new RunServiceTask()); |
| -} |
| - |
| -// A handler for a per-isolate request. |
| -// |
| -// If a handler returns true, the reply is complete and ready to be |
| -// posted. If a handler returns false, then it is responsible for |
| -// posting the reply (this can be used for asynchronous delegation of |
| -// the response handling). |
| -typedef bool (*IsolateMessageHandler)(Isolate* isolate, JSONStream* stream); |
| + EmbedderServiceHandler* next() const { return next_; } |
| + void set_next(EmbedderServiceHandler* next) { |
| + next_ = next; |
| + } |
| -struct IsolateMessageHandlerEntry { |
| - const char* method; |
| - IsolateMessageHandler handler; |
| + private: |
| + char* name_; |
| + Dart_ServiceRequestCallback callback_; |
| + void* user_data_; |
| + EmbedderServiceHandler* next_; |
| }; |
| -static IsolateMessageHandler FindIsolateMessageHandler(const char* method); |
| - |
| -// A handler for a root (vm-global) request. |
| -// |
| -// If a handler returns true, the reply is complete and ready to be |
| -// posted. If a handler returns false, then it is responsible for |
| -// posting the reply (this can be used for asynchronous delegation of |
| -// the response handling). |
| -typedef bool (*RootMessageHandler)(JSONStream* stream); |
| - |
| -struct RootMessageHandlerEntry { |
| - const char* method; |
| - RootMessageHandler handler; |
| -}; |
| - |
| -static RootMessageHandler FindRootMessageHandler(const char* method); |
| +void Service::EmbedderHandleMessage(EmbedderServiceHandler* handler, |
| + JSONStream* js) { |
| + ASSERT(handler != NULL); |
| + Dart_ServiceRequestCallback callback = handler->callback(); |
| + ASSERT(callback != NULL); |
| + const char* r = NULL; |
| + const char* name = js->method(); |
| + const char** keys = js->param_keys(); |
| + const char** values = js->param_values(); |
| + r = callback(name, keys, values, js->num_params(), handler->user_data()); |
| + ASSERT(r != NULL); |
| + // TODO(johnmccutchan): Allow for NULL returns? |
| + TextBuffer* buffer = js->buffer(); |
| + buffer->AddString(r); |
| + free(const_cast<char*>(r)); |
| +} |
| -static void PrintRequest(const JSONObject& obj, JSONStream* js) { |
| - JSONObject jsobj(&obj, "request"); |
| - jsobj.AddProperty("method", js->method()); |
| - { |
| - JSONArray jsarr(&jsobj, "param_keys"); |
| - for (intptr_t i = 0; i < js->num_params(); i++) { |
| - jsarr.AddValue(js->GetParamKey(i)); |
| - } |
| +void Service::RegisterIsolateEmbedderCallback( |
| + const char* name, |
| + Dart_ServiceRequestCallback callback, |
| + void* user_data) { |
| + if (name == NULL) { |
| + return; |
| } |
| - { |
| - JSONArray jsarr(&jsobj, "param_values"); |
| - for (intptr_t i = 0; i < js->num_params(); i++) { |
| - jsarr.AddValue(js->GetParamValue(i)); |
| - } |
| + EmbedderServiceHandler* handler = FindIsolateEmbedderHandler(name); |
| + if (handler != NULL) { |
| + // Update existing handler entry. |
| + handler->set_callback(callback); |
| + handler->set_user_data(user_data); |
| + return; |
| } |
| -} |
| - |
| - |
| -static void PrintError(JSONStream* js, |
| - const char* format, ...) { |
| - Isolate* isolate = Isolate::Current(); |
| - |
| - va_list args; |
| - va_start(args, format); |
| - intptr_t len = OS::VSNPrint(NULL, 0, format, args); |
| - va_end(args); |
| - |
| - char* buffer = isolate->current_zone()->Alloc<char>(len + 1); |
| - va_list args2; |
| - va_start(args2, format); |
| - OS::VSNPrint(buffer, (len + 1), format, args2); |
| - va_end(args2); |
| - |
| - JSONObject jsobj(js); |
| - jsobj.AddProperty("type", "Error"); |
| - jsobj.AddProperty("message", buffer); |
| - PrintRequest(jsobj, js); |
| -} |
| - |
| + // Create a new handler. |
| + handler = new EmbedderServiceHandler(name); |
| + handler->set_callback(callback); |
| + handler->set_user_data(user_data); |
| -static void PrintMissingParamError(JSONStream* js, |
| - const char* param) { |
| - PrintError(js, "%s expects the '%s' parameter", |
| - js->method(), param); |
| + // Insert into isolate_service_handler_head_ list. |
| + handler->set_next(isolate_service_handler_head_); |
| + isolate_service_handler_head_ = handler; |
| } |
| -static void PrintInvalidParamError(JSONStream* js, |
| - const char* param) { |
| - PrintError(js, "%s: invalid '%s' parameter: %s", |
| - js->method(), param, js->LookupParam(param)); |
| +EmbedderServiceHandler* Service::FindIsolateEmbedderHandler( |
| + const char* name) { |
| + EmbedderServiceHandler* current = isolate_service_handler_head_; |
| + while (current != NULL) { |
| + if (strcmp(name, current->name()) == 0) { |
| + return current; |
| + } |
| + current = current->next(); |
| + } |
| + return NULL; |
| } |
| -static void PrintErrorWithKind(JSONStream* js, |
| - const char* kind, |
| - const char* format, ...) { |
| - Isolate* isolate = Isolate::Current(); |
| - |
| - va_list args; |
| - va_start(args, format); |
| - intptr_t len = OS::VSNPrint(NULL, 0, format, args); |
| - va_end(args); |
| - |
| - char* buffer = isolate->current_zone()->Alloc<char>(len + 1); |
| - va_list args2; |
| - va_start(args2, format); |
| - OS::VSNPrint(buffer, (len + 1), format, args2); |
| - va_end(args2); |
| +void Service::RegisterRootEmbedderCallback( |
| + const char* name, |
| + Dart_ServiceRequestCallback callback, |
| + void* user_data) { |
| + if (name == NULL) { |
| + return; |
| + } |
| + EmbedderServiceHandler* handler = FindRootEmbedderHandler(name); |
| + if (handler != NULL) { |
| + // Update existing handler entry. |
| + handler->set_callback(callback); |
| + handler->set_user_data(user_data); |
| + return; |
| + } |
| + // Create a new handler. |
| + handler = new EmbedderServiceHandler(name); |
| + handler->set_callback(callback); |
| + handler->set_user_data(user_data); |
| - JSONObject jsobj(js); |
| - jsobj.AddProperty("type", "Error"); |
| - jsobj.AddProperty("id", ""); |
| - jsobj.AddProperty("kind", kind); |
| - jsobj.AddProperty("message", buffer); |
| - PrintRequest(jsobj, js); |
| + // Insert into root_service_handler_head_ list. |
| + handler->set_next(root_service_handler_head_); |
| + root_service_handler_head_ = handler; |
| } |
| -void Service::HandleIsolateMessage(Isolate* isolate, const Array& msg) { |
| - ASSERT(isolate != NULL); |
| - ASSERT(!msg.IsNull()); |
| - ASSERT(msg.Length() == 5); |
| - |
| - { |
| - StackZone zone(isolate); |
| - HANDLESCOPE(isolate); |
| - |
| - Instance& reply_port = Instance::Handle(isolate); |
| - String& method = String::Handle(isolate); |
| - Array& param_keys = Array::Handle(isolate); |
| - Array& param_values = Array::Handle(isolate); |
| - reply_port ^= msg.At(1); |
| - method ^= msg.At(2); |
| - param_keys ^= msg.At(3); |
| - param_values ^= msg.At(4); |
| - |
| - ASSERT(!method.IsNull()); |
| - ASSERT(!param_keys.IsNull()); |
| - ASSERT(!param_values.IsNull()); |
| - ASSERT(param_keys.Length() == param_values.Length()); |
| - |
| - if (!reply_port.IsSendPort()) { |
| - FATAL("SendPort expected."); |
| - } |
| - |
| - IsolateMessageHandler handler = |
| - FindIsolateMessageHandler(method.ToCString()); |
| - { |
| - JSONStream js; |
| - js.Setup(zone.GetZone(), SendPort::Cast(reply_port).Id(), |
| - method, param_keys, param_values); |
| - if (handler == NULL) { |
| - // Check for an embedder handler. |
| - EmbedderServiceHandler* e_handler = |
| - FindIsolateEmbedderHandler(method.ToCString()); |
| - if (e_handler != NULL) { |
| - EmbedderHandleMessage(e_handler, &js); |
| - } else { |
| - if (FindRootMessageHandler(method.ToCString()) != NULL) { |
| - PrintError(&js, "%s does not expect the 'isolateId' parameter", |
| - method.ToCString()); |
| - } else { |
| - PrintError(&js, "Unrecognized method: %s", method.ToCString()); |
| - } |
| - } |
| - js.PostReply(); |
| - } else { |
| - if (handler(isolate, &js)) { |
| - // Handler returns true if the reply is ready to be posted. |
| - // TODO(johnmccutchan): Support asynchronous replies. |
| - js.PostReply(); |
| - } |
| - } |
| +EmbedderServiceHandler* Service::FindRootEmbedderHandler( |
| + const char* name) { |
| + EmbedderServiceHandler* current = root_service_handler_head_; |
| + while (current != NULL) { |
| + if (strcmp(name, current->name()) == 0) { |
| + return current; |
| } |
| + current = current->next(); |
| } |
| + return NULL; |
| } |
| @@ -1048,112 +743,6 @@ static bool HandleIsolateEcho(Isolate* isolate, JSONStream* js) { |
| } |
| -static bool GetIntegerId(const char* s, intptr_t* id, int base = 10) { |
| - if ((s == NULL) || (*s == '\0')) { |
| - // Empty string. |
| - return false; |
| - } |
| - if (id == NULL) { |
| - // No id pointer. |
| - return false; |
| - } |
| - intptr_t r = 0; |
| - char* end_ptr = NULL; |
| - r = strtol(s, &end_ptr, base); |
| - if (end_ptr == s) { |
| - // String was not advanced at all, cannot be valid. |
| - return false; |
| - } |
| - *id = r; |
| - return true; |
| -} |
| - |
| - |
| -static bool GetUnsignedIntegerId(const char* s, uintptr_t* id, int base = 10) { |
| - if ((s == NULL) || (*s == '\0')) { |
| - // Empty string. |
| - return false; |
| - } |
| - if (id == NULL) { |
| - // No id pointer. |
| - return false; |
| - } |
| - uintptr_t r = 0; |
| - char* end_ptr = NULL; |
| - r = strtoul(s, &end_ptr, base); |
| - if (end_ptr == s) { |
| - // String was not advanced at all, cannot be valid. |
| - return false; |
| - } |
| - *id = r; |
| - return true; |
| -} |
| - |
| - |
| -static bool GetInteger64Id(const char* s, int64_t* id, int base = 10) { |
| - if ((s == NULL) || (*s == '\0')) { |
| - // Empty string. |
| - return false; |
| - } |
| - if (id == NULL) { |
| - // No id pointer. |
| - return false; |
| - } |
| - int64_t r = 0; |
| - char* end_ptr = NULL; |
| - r = strtoll(s, &end_ptr, base); |
| - if (end_ptr == s) { |
| - // String was not advanced at all, cannot be valid. |
| - return false; |
| - } |
| - *id = r; |
| - return true; |
| -} |
| - |
| -// Scans the string until the '-' character. Returns pointer to string |
| -// at '-' character. Returns NULL if not found. |
| -static const char* ScanUntilDash(const char* s) { |
| - if ((s == NULL) || (*s == '\0')) { |
| - // Empty string. |
| - return NULL; |
| - } |
| - while (*s != '\0') { |
| - if (*s == '-') { |
| - return s; |
| - } |
| - s++; |
| - } |
| - return NULL; |
| -} |
| - |
| - |
| -static bool GetCodeId(const char* s, int64_t* timestamp, uword* address) { |
| - if ((s == NULL) || (*s == '\0')) { |
| - // Empty string. |
| - return false; |
| - } |
| - if ((timestamp == NULL) || (address == NULL)) { |
| - // Bad arguments. |
| - return false; |
| - } |
| - // Extract the timestamp. |
| - if (!GetInteger64Id(s, timestamp, 16) || (*timestamp < 0)) { |
| - return false; |
| - } |
| - s = ScanUntilDash(s); |
| - if (s == NULL) { |
| - return false; |
| - } |
| - // Skip the dash. |
| - s++; |
| - // Extract the PC. |
| - if (!GetUnsignedIntegerId(s, address, 16)) { |
| - return false; |
| - } |
| - return true; |
| -} |
| - |
| - |
| static bool ContainsNonInstance(const Object& obj) { |
| if (obj.IsArray()) { |
| const Array& array = Array::Cast(obj); |
| @@ -1912,14 +1501,71 @@ static bool HandleIsolateGetInstances(Isolate* isolate, JSONStream* js) { |
| wrapper.SetLength(count); |
| storage = Array::MakeArray(wrapper); |
| } |
| - JSONObject jsobj(js); |
| - jsobj.AddProperty("type", "InstanceSet"); |
| - jsobj.AddProperty("id", "instance_set"); |
| - jsobj.AddProperty("totalCount", count); |
| - jsobj.AddProperty("sampleCount", storage.Length()); |
| - jsobj.AddProperty("sample", storage); |
| - return true; |
| -} |
| + JSONObject jsobj(js); |
| + jsobj.AddProperty("type", "InstanceSet"); |
| + jsobj.AddProperty("id", "instance_set"); |
| + jsobj.AddProperty("totalCount", count); |
| + jsobj.AddProperty("sampleCount", storage.Length()); |
| + jsobj.AddProperty("sample", storage); |
| + return true; |
| +} |
| + |
| + |
| +class LibraryCoverageFilter : public CoverageFilter { |
| + public: |
| + explicit LibraryCoverageFilter(const Library& lib) : lib_(lib) {} |
| + bool ShouldOutputCoverageFor(const Library& lib, |
| + const Script& script, |
| + const Class& cls, |
| + const Function& func) const { |
| + return lib.raw() == lib_.raw(); |
| + } |
| + private: |
| + const Library& lib_; |
| +}; |
| + |
| + |
| +class ScriptCoverageFilter : public CoverageFilter { |
| + public: |
| + explicit ScriptCoverageFilter(const Script& script) |
| + : script_(script) {} |
| + bool ShouldOutputCoverageFor(const Library& lib, |
| + const Script& script, |
| + const Class& cls, |
| + const Function& func) const { |
| + return script.raw() == script_.raw(); |
| + } |
| + private: |
| + const Script& script_; |
| +}; |
| + |
| + |
| +class ClassCoverageFilter : public CoverageFilter { |
| + public: |
| + explicit ClassCoverageFilter(const Class& cls) : cls_(cls) {} |
| + bool ShouldOutputCoverageFor(const Library& lib, |
| + const Script& script, |
| + const Class& cls, |
| + const Function& func) const { |
| + return cls.raw() == cls_.raw(); |
| + } |
| + private: |
| + const Class& cls_; |
| +}; |
| + |
| + |
| +class FunctionCoverageFilter : public CoverageFilter { |
| + public: |
| + explicit FunctionCoverageFilter(const Function& func) : func_(func) {} |
| + bool ShouldOutputCoverageFor(const Library& lib, |
| + const Script& script, |
| + const Class& cls, |
| + const Function& func) const { |
| + return func.raw() == func_.raw(); |
| + } |
| + private: |
| + const Function& func_; |
| +}; |
| static bool HandleIsolateGetCoverage(Isolate* isolate, JSONStream* js) { |
| @@ -2155,12 +1801,12 @@ static bool HandleIsolateGetMetric(Isolate* isolate, JSONStream* js) { |
| } |
| -static bool HandleVMGetMetricList(JSONStream* js) { |
| +static bool HandleVMGetMetricList(Isolate* isolate, JSONStream* js) { |
| return false; |
| } |
| -static bool HandleVMGetMetric(JSONStream* js) { |
| +static bool HandleVMGetMetric(Isolate* isolate, JSONStream* js) { |
| const char* metric_id = js->LookupParam("metricId"); |
| if (metric_id == NULL) { |
| PrintMissingParamError(js, "metricId"); |
| @@ -2242,24 +1888,37 @@ static bool HandleIsolateGetTagProfile(Isolate* isolate, JSONStream* js) { |
| return true; |
| } |
| + |
| +static const char* tags_enum_names[] = { |
| + "None", |
| + "UserVM", |
| + "UserOnly", |
| + "VMUser", |
| + "VMOnly", |
| + NULL, |
| +}; |
| + |
| + |
| +static ProfilerService::TagOrder tags_enum_values[] = { |
| + ProfilerService::kNoTags, |
| + ProfilerService::kUserVM, |
| + ProfilerService::kUser, |
| + ProfilerService::kVMUser, |
| + ProfilerService::kVM, |
| + ProfilerService::kNoTags, // Default value. |
| +}; |
| + |
| + |
| +static const MethodParameter* cpu_profile_params[] = { |
| + ISOLATE_PARAMETER, |
| + new EnumParameter("tags", true, tags_enum_names), |
| + NULL, |
| +}; |
| + |
| + |
| static bool HandleIsolateGetCpuProfile(Isolate* isolate, JSONStream* js) { |
| - ProfilerService::TagOrder tag_order = ProfilerService::kUserVM; |
| - if (js->HasParam("tags")) { |
| - if (js->ParamIs("tags", "None")) { |
| - tag_order = ProfilerService::kNoTags; |
| - } else if (js->ParamIs("tags", "UserVM")) { |
| - tag_order = ProfilerService::kUserVM; |
| - } else if (js->ParamIs("tags", "UserOnly")) { |
| - tag_order = ProfilerService::kUser; |
| - } else if (js->ParamIs("tags", "VMUser")) { |
| - tag_order = ProfilerService::kVMUser; |
| - } else if (js->ParamIs("tags", "VMOnly")) { |
| - tag_order = ProfilerService::kVM; |
| - } else { |
| - PrintInvalidParamError(js, "tags"); |
| - return true; |
| - } |
| - } |
| + ProfilerService::TagOrder tag_order = |
| + EnumMapper(js->LookupParam("tags"), tags_enum_names, tags_enum_values); |
| ProfilerService::PrintJSON(js, tag_order); |
| return true; |
| } |
| @@ -2473,122 +2132,6 @@ static bool HandleIsolateGetTypeArgumentsList(Isolate* isolate, |
| } |
| -static IsolateMessageHandlerEntry isolate_handlers_new[] = { |
| - { "getIsolate", HandleIsolate }, |
| - { "getObject", HandleIsolateGetObject }, |
| - { "getObjectByAddress", HandleIsolateGetObjectByAddress }, |
| - { "getBreakpoints", HandleIsolateGetBreakpoints }, |
| - { "pause", HandleIsolatePause }, |
| - { "resume", HandleIsolateResume }, |
| - { "getStack", HandleIsolateGetStack }, |
| - { "getCpuProfile", HandleIsolateGetCpuProfile }, |
| - { "getTagProfile", HandleIsolateGetTagProfile }, |
| - { "getAllocationProfile", HandleIsolateGetAllocationProfile }, |
| - { "getHeapMap", HandleIsolateGetHeapMap }, |
| - { "addBreakpoint", HandleIsolateAddBreakpoint }, |
| - { "removeBreakpoint", HandleIsolateRemoveBreakpoint }, |
| - { "getCoverage", HandleIsolateGetCoverage }, |
| - { "eval", HandleIsolateEval }, |
| - { "getRetainedSize", HandleIsolateGetRetainedSize }, |
| - { "getRetainingPath", HandleIsolateGetRetainingPath }, |
| - { "getInboundReferences", HandleIsolateGetInboundReferences }, |
| - { "getInstances", HandleIsolateGetInstances }, |
| - { "requestHeapSnapshot", HandleIsolateRequestHeapSnapshot }, |
| - { "getClassList", HandleIsolateGetClassList }, |
| - { "getTypeArgumentsList", HandleIsolateGetTypeArgumentsList }, |
| - { "getIsolateMetricList", HandleIsolateGetMetricList }, |
| - { "getIsolateMetric", HandleIsolateGetMetric }, |
| - { "_echo", HandleIsolateEcho }, |
| - { "_triggerEchoEvent", HandleIsolateTriggerEchoEvent }, |
| - { "_respondWithMalformedJson", HandleIsolateRespondWithMalformedJson }, |
| - { "_respondWithMalformedObject", HandleIsolateRespondWithMalformedObject }, |
| -}; |
| - |
| - |
| -static IsolateMessageHandler FindIsolateMessageHandler(const char* method) { |
| - intptr_t num_message_handlers = sizeof(isolate_handlers_new) / |
| - sizeof(isolate_handlers_new[0]); |
| - for (intptr_t i = 0; i < num_message_handlers; i++) { |
| - const IsolateMessageHandlerEntry& entry = isolate_handlers_new[i]; |
| - if (strcmp(method, entry.method) == 0) { |
| - return entry.handler; |
| - } |
| - } |
| - if (FLAG_trace_service) { |
| - OS::Print("Service has no isolate message handler for <%s>\n", method); |
| - } |
| - return NULL; |
| -} |
| - |
| - |
| -void Service::HandleRootMessage(const Instance& msg_instance) { |
| - Isolate* isolate = Isolate::Current(); |
| - ASSERT(!msg_instance.IsNull()); |
| - ASSERT(msg_instance.IsArray()); |
| - |
| - { |
| - StackZone zone(isolate); |
| - HANDLESCOPE(isolate); |
| - |
| - const Array& msg = Array::Cast(msg_instance); |
| - ASSERT(msg.Length() == 5); |
| - |
| - Instance& reply_port = Instance::Handle(isolate); |
| - String& method = String::Handle(isolate); |
| - Array& param_keys = Array::Handle(isolate); |
| - Array& param_values = Array::Handle(isolate); |
| - reply_port ^= msg.At(1); |
| - method ^= msg.At(2); |
| - param_keys ^= msg.At(3); |
| - param_values ^= msg.At(4); |
| - |
| - ASSERT(!method.IsNull()); |
| - ASSERT(!param_keys.IsNull()); |
| - ASSERT(!param_values.IsNull()); |
| - ASSERT(param_keys.Length() == param_values.Length()); |
| - |
| - if (!reply_port.IsSendPort()) { |
| - FATAL("SendPort expected."); |
| - } |
| - |
| - RootMessageHandler handler = |
| - FindRootMessageHandler(method.ToCString()); |
| - { |
| - JSONStream js; |
| - js.Setup(zone.GetZone(), SendPort::Cast(reply_port).Id(), |
| - method, param_keys, param_values); |
| - if (handler == NULL) { |
| - // Check for an embedder handler. |
| - EmbedderServiceHandler* e_handler = |
| - FindRootEmbedderHandler(method.ToCString()); |
| - if (e_handler != NULL) { |
| - EmbedderHandleMessage(e_handler, &js); |
| - } else { |
| - if (FindIsolateMessageHandler(method.ToCString()) != NULL) { |
| - PrintMissingParamError(&js, "isolateId"); |
| - } else { |
| - PrintError(&js, "Unrecognized method: %s", method.ToCString()); |
| - } |
| - } |
| - js.PostReply(); |
| - } else { |
| - if (handler(&js)) { |
| - // Handler returns true if the reply is ready to be posted. |
| - // TODO(johnmccutchan): Support asynchronous replies. |
| - js.PostReply(); |
| - } |
| - } |
| - } |
| - } |
| -} |
| - |
| - |
| -static bool HandleRootEcho(JSONStream* js) { |
| - JSONObject jsobj(js); |
| - return HandleCommonEcho(&jsobj, js); |
| -} |
| - |
| - |
| class ServiceIsolateVisitor : public IsolateVisitor { |
| public: |
| explicit ServiceIsolateVisitor(JSONArray* jsarr) |
| @@ -2598,7 +2141,8 @@ class ServiceIsolateVisitor : public IsolateVisitor { |
| virtual ~ServiceIsolateVisitor() {} |
| void VisitIsolate(Isolate* isolate) { |
| - if (isolate != Dart::vm_isolate() && !Service::IsServiceIsolate(isolate)) { |
| + if ((isolate != Dart::vm_isolate()) && |
| + !ServiceIsolate::IsServiceIsolate(isolate)) { |
| jsarr_->AddValue(isolate); |
| } |
| } |
| @@ -2608,8 +2152,7 @@ class ServiceIsolateVisitor : public IsolateVisitor { |
| }; |
| -static bool HandleVM(JSONStream* js) { |
| - Isolate* isolate = Isolate::Current(); |
| +static bool HandleVM(Isolate* isolate, JSONStream* js) { |
| JSONObject jsobj(js); |
| jsobj.AddProperty("type", "VM"); |
| jsobj.AddProperty("id", "vm"); |
| @@ -2640,13 +2183,13 @@ static bool HandleVM(JSONStream* js) { |
| } |
| -static bool HandleVMFlagList(JSONStream* js) { |
| +static bool HandleVMFlagList(Isolate* isolate, JSONStream* js) { |
| Flags::PrintJSON(js); |
| return true; |
| } |
| -static bool HandleVMSetFlag(JSONStream* js) { |
| +static bool HandleVMSetFlag(Isolate* isolate, JSONStream* js) { |
| const char* flag_name = js->LookupParam("name"); |
| if (flag_name == NULL) { |
| PrintMissingParamError(js, "name"); |
| @@ -2672,198 +2215,55 @@ static bool HandleVMSetFlag(JSONStream* js) { |
| } |
| -static RootMessageHandlerEntry root_handlers_new[] = { |
| - { "getVM", HandleVM }, |
| - { "getFlagList", HandleVMFlagList }, |
| - { "setFlag", HandleVMSetFlag }, |
| - { "getVMMetricList", HandleVMGetMetricList }, |
| - { "getVMMetric", HandleVMGetMetric }, |
| - { "_echo", HandleRootEcho }, |
| +static ServiceMethodDescriptor service_methods_[] = { |
| + { "_echo", HandleIsolateEcho, NULL }, |
| + { "_respondWithMalformedJson", HandleIsolateRespondWithMalformedJson, NULL }, |
| + { "_respondWithMalformedObject", |
| + HandleIsolateRespondWithMalformedObject, NULL }, |
| + { "_triggerEchoEvent", HandleIsolateTriggerEchoEvent, NULL }, |
| + { "addBreakpoint", HandleIsolateAddBreakpoint, NULL }, |
| + { "eval", HandleIsolateEval, NULL }, |
| + { "getAllocationProfile", HandleIsolateGetAllocationProfile, NULL }, |
| + { "getBreakpoints", HandleIsolateGetBreakpoints, NULL }, |
| + { "getClassList", HandleIsolateGetClassList, NULL }, |
| + { "getCoverage", HandleIsolateGetCoverage, NULL }, |
| + { "getCpuProfile", HandleIsolateGetCpuProfile, cpu_profile_params, }, |
|
turnidge
2015/02/12 18:08:57
Would be nice to add validators for everything as
Cutch
2015/02/12 18:18:47
Partially done. Only the isolate parameter is list
|
| + { "getFlagList", HandleVMFlagList , NULL}, |
| + { "getHeapMap", HandleIsolateGetHeapMap, NULL }, |
| + { "getInboundReferences", HandleIsolateGetInboundReferences, NULL }, |
| + { "getInstances", HandleIsolateGetInstances, NULL }, |
| + { "getIsolate", HandleIsolate, NULL }, |
| + { "getIsolateMetric", HandleIsolateGetMetric, NULL }, |
| + { "getIsolateMetricList", HandleIsolateGetMetricList, NULL }, |
| + { "getObject", HandleIsolateGetObject, NULL }, |
| + { "getObjectByAddress", HandleIsolateGetObjectByAddress, NULL }, |
| + { "getRetainedSize", HandleIsolateGetRetainedSize, NULL }, |
| + { "getRetainingPath", HandleIsolateGetRetainingPath, NULL }, |
| + { "getStack", HandleIsolateGetStack, NULL }, |
| + { "getTagProfile", HandleIsolateGetTagProfile, NULL }, |
| + { "getTypeArgumentsList", HandleIsolateGetTypeArgumentsList, NULL }, |
| + { "getVM", HandleVM , NULL}, |
| + { "getVMMetric", HandleVMGetMetric , NULL}, |
| + { "getVMMetricList", HandleVMGetMetricList , NULL}, |
| + { "pause", HandleIsolatePause, NULL }, |
| + { "removeBreakpoint", HandleIsolateRemoveBreakpoint, NULL }, |
| + { "resume", HandleIsolateResume, NULL }, |
| + { "requestHeapSnapshot", HandleIsolateRequestHeapSnapshot, NULL }, |
| + { "setFlag", HandleVMSetFlag , NULL}, |
| }; |
| -static RootMessageHandler FindRootMessageHandler(const char* method) { |
| - intptr_t num_message_handlers = sizeof(root_handlers_new) / |
| - sizeof(root_handlers_new[0]); |
| - for (intptr_t i = 0; i < num_message_handlers; i++) { |
| - const RootMessageHandlerEntry& entry = root_handlers_new[i]; |
| - if (strcmp(method, entry.method) == 0) { |
| - return entry.handler; |
| - } |
| - } |
| - if (FLAG_trace_service) { |
| - OS::Print("vm-service: No root message handler for <%s>.\n", method); |
| - } |
| - return NULL; |
| -} |
| - |
| - |
| -void Service::SendEvent(intptr_t eventId, const Object& eventMessage) { |
| - if (!IsRunning()) { |
| - return; |
| - } |
| - Isolate* isolate = Isolate::Current(); |
| - ASSERT(isolate != NULL); |
| - HANDLESCOPE(isolate); |
| - |
| - // Construct a list of the form [eventId, eventMessage]. |
| - const Array& list = Array::Handle(Array::New(2)); |
| - ASSERT(!list.IsNull()); |
| - list.SetAt(0, Integer::Handle(Integer::New(eventId))); |
| - list.SetAt(1, eventMessage); |
| - |
| - // Push the event to port_. |
| - uint8_t* data = NULL; |
| - MessageWriter writer(&data, &allocator, false); |
| - writer.WriteMessage(list); |
| - intptr_t len = writer.BytesWritten(); |
| - if (FLAG_trace_service) { |
| - OS::Print("vm-service: Pushing event of type %" Pd ", len %" Pd "\n", |
| - eventId, len); |
| - } |
| - // TODO(turnidge): For now we ignore failure to send an event. Revisit? |
| - PortMap::PostMessage( |
| - new Message(service_port_, data, len, Message::kNormalPriority)); |
| -} |
| - |
| - |
| -void Service::SendEvent(intptr_t eventId, |
| - const String& meta, |
| - const uint8_t* data, |
| - intptr_t size) { |
| - // Bitstream: [meta data size (big-endian 64 bit)] [meta data (UTF-8)] [data] |
| - const intptr_t meta_bytes = Utf8::Length(meta); |
| - const intptr_t total_bytes = sizeof(uint64_t) + meta_bytes + size; |
| - const TypedData& message = TypedData::Handle( |
| - TypedData::New(kTypedDataUint8ArrayCid, total_bytes)); |
| - intptr_t offset = 0; |
| - // TODO(koda): Rename these methods SetHostUint64, etc. |
| - message.SetUint64(0, Utils::HostToBigEndian64(meta_bytes)); |
| - offset += sizeof(uint64_t); |
| - { |
| - NoGCScope no_gc; |
| - meta.ToUTF8(static_cast<uint8_t*>(message.DataAddr(offset)), meta_bytes); |
| - offset += meta_bytes; |
| - } |
| - // TODO(koda): It would be nice to avoid this copy (requires changes to |
| - // MessageWriter code). |
| - { |
| - NoGCScope no_gc; |
| - memmove(message.DataAddr(offset), data, size); |
| - offset += size; |
| - } |
| - ASSERT(offset == total_bytes); |
| - SendEvent(eventId, message); |
| -} |
| - |
| - |
| -void Service::HandleGCEvent(GCEvent* event) { |
| - JSONStream js; |
| - event->PrintJSON(&js); |
| - const String& message = String::Handle(String::New(js.ToCString())); |
| - SendEvent(kEventFamilyGC, message); |
| -} |
| - |
| - |
| -void Service::HandleDebuggerEvent(DebuggerEvent* event) { |
| - JSONStream js; |
| - event->PrintJSON(&js); |
| - const String& message = String::Handle(String::New(js.ToCString())); |
| - SendEvent(kEventFamilyDebug, message); |
| -} |
| - |
| - |
| -void Service::EmbedderHandleMessage(EmbedderServiceHandler* handler, |
| - JSONStream* js) { |
| - ASSERT(handler != NULL); |
| - Dart_ServiceRequestCallback callback = handler->callback(); |
| - ASSERT(callback != NULL); |
| - const char* r = NULL; |
| - const char* name = js->method(); |
| - const char** keys = js->param_keys(); |
| - const char** values = js->param_values(); |
| - r = callback(name, keys, values, js->num_params(), handler->user_data()); |
| - ASSERT(r != NULL); |
| - // TODO(johnmccutchan): Allow for NULL returns? |
| - TextBuffer* buffer = js->buffer(); |
| - buffer->AddString(r); |
| - free(const_cast<char*>(r)); |
| -} |
| - |
| - |
| -void Service::RegisterIsolateEmbedderCallback( |
| - const char* name, |
| - Dart_ServiceRequestCallback callback, |
| - void* user_data) { |
| - if (name == NULL) { |
| - return; |
| - } |
| - EmbedderServiceHandler* handler = FindIsolateEmbedderHandler(name); |
| - if (handler != NULL) { |
| - // Update existing handler entry. |
| - handler->set_callback(callback); |
| - handler->set_user_data(user_data); |
| - return; |
| - } |
| - // Create a new handler. |
| - handler = new EmbedderServiceHandler(name); |
| - handler->set_callback(callback); |
| - handler->set_user_data(user_data); |
| - |
| - // Insert into isolate_service_handler_head_ list. |
| - handler->set_next(isolate_service_handler_head_); |
| - isolate_service_handler_head_ = handler; |
| -} |
| - |
| - |
| -EmbedderServiceHandler* Service::FindIsolateEmbedderHandler( |
| - const char* name) { |
| - EmbedderServiceHandler* current = isolate_service_handler_head_; |
| - while (current != NULL) { |
| - if (strcmp(name, current->name()) == 0) { |
| - return current; |
| +ServiceMethodDescriptor* FindMethod(const char* method_name) { |
| + intptr_t num_methods = sizeof(service_methods_) / |
| + sizeof(service_methods_[0]); |
| + for (intptr_t i = 0; i < num_methods; i++) { |
| + ServiceMethodDescriptor& method = service_methods_[i]; |
| + if (strcmp(method_name, method.name) == 0) { |
| + return &method; |
| } |
| - current = current->next(); |
| } |
| return NULL; |
| } |
| -void Service::RegisterRootEmbedderCallback( |
| - const char* name, |
| - Dart_ServiceRequestCallback callback, |
| - void* user_data) { |
| - if (name == NULL) { |
| - return; |
| - } |
| - EmbedderServiceHandler* handler = FindRootEmbedderHandler(name); |
| - if (handler != NULL) { |
| - // Update existing handler entry. |
| - handler->set_callback(callback); |
| - handler->set_user_data(user_data); |
| - return; |
| - } |
| - // Create a new handler. |
| - handler = new EmbedderServiceHandler(name); |
| - handler->set_callback(callback); |
| - handler->set_user_data(user_data); |
| - |
| - // Insert into root_service_handler_head_ list. |
| - handler->set_next(root_service_handler_head_); |
| - root_service_handler_head_ = handler; |
| -} |
| - |
| - |
| -EmbedderServiceHandler* Service::FindRootEmbedderHandler( |
| - const char* name) { |
| - EmbedderServiceHandler* current = root_service_handler_head_; |
| - while (current != NULL) { |
| - if (strcmp(name, current->name()) == 0) { |
| - return current; |
| - } |
| - current = current->next(); |
| - } |
| - return NULL; |
| -} |
| - |
| } // namespace dart |