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())); |