| Index: runtime/vm/service.cc
|
| diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
|
| index f4241bb996cbbb7a1a0eabe689732815c8eeda42..0d134e86371811d3bcae2afc6087014e140ebe10 100644
|
| --- a/runtime/vm/service.cc
|
| +++ b/runtime/vm/service.cc
|
| @@ -15,12 +15,13 @@
|
| #include "vm/dart_entry.h"
|
| #include "vm/debugger.h"
|
| #include "vm/isolate.h"
|
| +#include "vm/kernel_isolate.h"
|
| #include "vm/lockers.h"
|
| #include "vm/malloc_hooks.h"
|
| #include "vm/message.h"
|
| #include "vm/message_handler.h"
|
| -#include "vm/native_entry.h"
|
| #include "vm/native_arguments.h"
|
| +#include "vm/native_entry.h"
|
| #include "vm/native_symbol.h"
|
| #include "vm/object.h"
|
| #include "vm/object_graph.h"
|
| @@ -40,7 +41,6 @@
|
| #include "vm/type_table.h"
|
| #include "vm/unicode.h"
|
| #include "vm/version.h"
|
| -#include "vm/kernel_isolate.h"
|
|
|
| namespace dart {
|
|
|
| @@ -2221,6 +2221,113 @@ static const MethodParameter* evaluate_params[] = {
|
| };
|
|
|
|
|
| +static bool IsAlpha(char c) {
|
| + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
|
| +}
|
| +static bool IsAlphaNum(char c) {
|
| + return (c >= '0' && c <= '9') || IsAlpha(c);
|
| +}
|
| +static bool IsWhitespace(char c) {
|
| + return c <= ' ';
|
| +}
|
| +static bool IsObjectIdChar(char c) {
|
| + return IsAlphaNum(c) || c == '/' || c == '-' || c == '@' || c == '%';
|
| +}
|
| +
|
| +
|
| +// TODO(vm-service): Consider whether we should pass structured objects in
|
| +// service messages instead of always flattening them to C strings.
|
| +static bool ParseScope(const char* scope,
|
| + GrowableArray<const char*>* names,
|
| + GrowableArray<const char*>* ids) {
|
| + Zone* zone = Thread::Current()->zone();
|
| + const char* c = scope;
|
| + if (*c++ != '{') return false;
|
| +
|
| + for (;;) {
|
| + while (IsWhitespace(*c)) {
|
| + c++;
|
| + }
|
| +
|
| + if (*c == '}') return true;
|
| +
|
| + const char* name = c;
|
| + if (!IsAlpha(*c)) return false;
|
| + while (IsAlphaNum(*c)) {
|
| + c++;
|
| + }
|
| + names->Add(zone->MakeCopyOfStringN(name, c - name));
|
| +
|
| + while (IsWhitespace(*c)) {
|
| + c++;
|
| + }
|
| +
|
| + if (*c++ != ':') return false;
|
| +
|
| + while (IsWhitespace(*c)) {
|
| + c++;
|
| + }
|
| +
|
| + const char* id = c;
|
| + if (!IsObjectIdChar(*c)) return false;
|
| + while (IsObjectIdChar(*c)) {
|
| + c++;
|
| + }
|
| + ids->Add(zone->MakeCopyOfStringN(id, c - id));
|
| +
|
| + while (IsWhitespace(*c)) {
|
| + c++;
|
| + }
|
| + if (*c == ',') c++;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +
|
| +static bool BuildScope(Thread* thread,
|
| + JSONStream* js,
|
| + const GrowableObjectArray& names,
|
| + const GrowableObjectArray& values) {
|
| + const char* scope = js->LookupParam("scope");
|
| + GrowableArray<const char*> cnames;
|
| + GrowableArray<const char*> cids;
|
| + if (scope != NULL) {
|
| + if (!ParseScope(scope, &cnames, &cids)) {
|
| + PrintInvalidParamError(js, "scope");
|
| + return true;
|
| + }
|
| + String& name = String::Handle();
|
| + Object& obj = Object::Handle();
|
| + for (intptr_t i = 0; i < cids.length(); i++) {
|
| + ObjectIdRing::LookupResult lookup_result;
|
| + obj = LookupHeapObject(thread, cids[i], &lookup_result);
|
| + if (obj.raw() == Object::sentinel().raw()) {
|
| + if (lookup_result == ObjectIdRing::kCollected) {
|
| + PrintSentinel(js, kCollectedSentinel);
|
| + } else if (lookup_result == ObjectIdRing::kExpired) {
|
| + PrintSentinel(js, kExpiredSentinel);
|
| + } else {
|
| + PrintInvalidParamError(js, "targetId");
|
| + }
|
| + return true;
|
| + }
|
| + if ((!obj.IsInstance() && !obj.IsNull()) || ContainsNonInstance(obj)) {
|
| + js->PrintError(kInvalidParams,
|
| + "%s: invalid scope 'targetId' parameter: "
|
| + "Cannot evaluate against a VM-internal object",
|
| + js->method());
|
| + return true;
|
| + }
|
| + name = String::New(cnames[i]);
|
| + names.Add(name);
|
| + values.Add(obj);
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +
|
| static bool Evaluate(Thread* thread, JSONStream* js) {
|
| if (!thread->isolate()->compilation_allowed()) {
|
| js->PrintError(kFeatureDisabled,
|
| @@ -2237,7 +2344,18 @@ static bool Evaluate(Thread* thread, JSONStream* js) {
|
| PrintMissingParamError(js, "expression");
|
| return true;
|
| }
|
| +
|
| Zone* zone = thread->zone();
|
| + const GrowableObjectArray& names =
|
| + GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
|
| + const GrowableObjectArray& values =
|
| + GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
|
| + if (BuildScope(thread, js, names, values)) {
|
| + return true;
|
| + }
|
| + const Array& names_array = Array::Handle(zone, Array::MakeArray(names));
|
| + const Array& values_array = Array::Handle(zone, Array::MakeArray(values));
|
| +
|
| const String& expr_str = String::Handle(zone, String::New(expr));
|
| ObjectIdRing::LookupResult lookup_result;
|
| Object& obj =
|
| @@ -2254,17 +2372,15 @@ static bool Evaluate(Thread* thread, JSONStream* js) {
|
| }
|
| if (obj.IsLibrary()) {
|
| const Library& lib = Library::Cast(obj);
|
| - const Object& result = Object::Handle(
|
| - zone,
|
| - lib.Evaluate(expr_str, Array::empty_array(), Array::empty_array()));
|
| + const Object& result =
|
| + Object::Handle(zone, lib.Evaluate(expr_str, names_array, values_array));
|
| result.PrintJSON(js, true);
|
| return true;
|
| }
|
| if (obj.IsClass()) {
|
| const Class& cls = Class::Cast(obj);
|
| - const Object& result = Object::Handle(
|
| - zone,
|
| - cls.Evaluate(expr_str, Array::empty_array(), Array::empty_array()));
|
| + const Object& result =
|
| + Object::Handle(zone, cls.Evaluate(expr_str, names_array, values_array));
|
| result.PrintJSON(js, true);
|
| return true;
|
| }
|
| @@ -2274,8 +2390,8 @@ static bool Evaluate(Thread* thread, JSONStream* js) {
|
| instance ^= obj.raw();
|
| const Class& receiver_cls = Class::Handle(zone, instance.clazz());
|
| const Object& result = Object::Handle(
|
| - zone, instance.Evaluate(receiver_cls, expr_str, Array::empty_array(),
|
| - Array::empty_array()));
|
| + zone,
|
| + instance.Evaluate(receiver_cls, expr_str, names_array, values_array));
|
| result.PrintJSON(js, true);
|
| return true;
|
| }
|
| @@ -2309,10 +2425,19 @@ static bool EvaluateInFrame(Thread* thread, JSONStream* js) {
|
| ActivationFrame* frame = stack->FrameAt(framePos);
|
|
|
| Zone* zone = thread->zone();
|
| + const GrowableObjectArray& names =
|
| + GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
|
| + const GrowableObjectArray& values =
|
| + GrowableObjectArray::Handle(zone, GrowableObjectArray::New());
|
| + if (BuildScope(thread, js, names, values)) {
|
| + return true;
|
| + }
|
| +
|
| const char* expr = js->LookupParam("expression");
|
| const String& expr_str = String::Handle(zone, String::New(expr));
|
|
|
| - const Object& result = Object::Handle(zone, frame->Evaluate(expr_str));
|
| + const Object& result =
|
| + Object::Handle(zone, frame->Evaluate(expr_str, names, values));
|
| result.PrintJSON(js, true);
|
| return true;
|
| }
|
| @@ -2929,9 +3054,10 @@ static const char* const timeline_streams_enum_names[] = {
|
| NULL};
|
|
|
| static const MethodParameter* set_vm_timeline_flags_params[] = {
|
| - NO_ISOLATE_PARAMETER, new EnumListParameter("recordedStreams",
|
| - false,
|
| - timeline_streams_enum_names),
|
| + NO_ISOLATE_PARAMETER,
|
| + new EnumListParameter("recordedStreams",
|
| + false,
|
| + timeline_streams_enum_names),
|
| NULL,
|
| };
|
|
|
| @@ -3609,8 +3735,9 @@ class PersistentHandleVisitor : public HandleVisitor {
|
| obj.AddProperty("type", "_WeakPersistentHandle");
|
| const Object& object = Object::Handle(weak_persistent_handle->raw());
|
| obj.AddProperty("object", object);
|
| - obj.AddPropertyF("peer", "0x%" Px "", reinterpret_cast<uintptr_t>(
|
| - weak_persistent_handle->peer()));
|
| + obj.AddPropertyF(
|
| + "peer", "0x%" Px "",
|
| + reinterpret_cast<uintptr_t>(weak_persistent_handle->peer()));
|
| obj.AddPropertyF(
|
| "callbackAddress", "0x%" Px "",
|
| reinterpret_cast<uintptr_t>(weak_persistent_handle->callback()));
|
|
|