Chromium Code Reviews| Index: runtime/vm/service.cc |
| diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc |
| index 4735b51036403ae329e2e5fc0c99c7b2f773dbc1..99fb9eeb3a1909e0d1d76763a6e560cd10465065 100644 |
| --- a/runtime/vm/service.cc |
| +++ b/runtime/vm/service.cc |
| @@ -30,6 +30,7 @@ |
| #include "vm/reusable_handles.h" |
| #include "vm/service_event.h" |
| #include "vm/service_isolate.h" |
| +#include "vm/source_report.h" |
| #include "vm/stack_frame.h" |
| #include "vm/symbols.h" |
| #include "vm/unicode.h" |
| @@ -561,6 +562,105 @@ T EnumMapper(const char* value, const char* const* enums, T* values) { |
| } |
| +class EnumListParameter : public MethodParameter { |
| + public: |
| + EnumListParameter(const char* name, bool required, const char* const* enums) |
| + : MethodParameter(name, required), |
| + enums_(enums) { |
| + } |
| + |
| + virtual bool Validate(const char* value) const { |
| + return ElementCount(value, enums_) >= 0; |
| + } |
| + |
| + static const char** Parse(Zone* zone, |
| + const char* value_in, |
| + const char* const* enums) { |
|
Cutch
2016/01/06 19:18:26
Why pass in enums again? You store it in the const
turnidge
2016/01/06 20:58:13
The rest of our Parse methods are static and I was
|
| + const char* kJsonChars = " \t\r\n[,]"; |
| + |
| + // Make a writeable copy of the value. |
| + char* value = zone->MakeCopyOfString(value_in); |
| + intptr_t element_count = ElementCount(value, enums); |
| + intptr_t element_pos = 0; |
| + |
| + // Allocate our element array. +1 for NULL terminator. |
| + char** elements = zone->Alloc<char*>(element_count + 1); |
| + elements[element_count] = NULL; |
| + |
| + // Parse the string destructively. Build the list of elements. |
|
Cutch
2016/01/06 19:18:26
Zone allocation is cheap, consider doing this with
turnidge
2016/01/06 20:58:12
This already is a copy of the string, check out th
|
| + while (element_pos < element_count) { |
| + // Skip to the next element. |
| + value += strspn(value, kJsonChars); |
| + |
| + intptr_t len = strcspn(value, kJsonChars); |
| + ASSERT(len > 0); // We rely on the parameter being validated already. |
| + value[len] = '\0'; |
| + elements[element_pos++] = value; |
| + |
| + // Advance. +1 for null terminator. |
| + value += (len + 1); |
| + } |
| + return const_cast<const char**>(elements); |
| + } |
| + |
| + private: |
| + // Returns number of elements in the list. -1 on parse error. |
| + static intptr_t ElementCount(const char* value, const char* const* enums) { |
| + if (value == NULL) { |
| + return -1; |
| + } |
| + const char* cp = value; |
|
Cutch
2016/01/06 19:18:26
Skip whitespace before checking for '['
turnidge
2016/01/06 20:58:12
The whitespace will be trimmed already by the time
|
| + if (*cp++ != '[') { |
| + // Missing initial [. |
| + return -1; |
| + } |
| + bool closed = false; |
| + bool element_allowed = true; |
| + intptr_t element_count = 0; |
| + while (true) { |
| + // Skip json whitespace. |
| + cp += strspn(cp, " \t\r\n"); |
|
Cutch
2016/01/06 19:18:25
Pull this constant string out to a kWhitespaceChar
turnidge
2016/01/06 20:58:12
Done.
|
| + switch (*cp) { |
| + case '\0': |
| + return closed ? element_count : -1; |
| + case ']': |
| + closed = true; |
| + cp++; |
| + break; |
| + case ',': |
| + if (element_allowed) { |
| + return -1; |
| + } |
| + element_allowed = true; |
| + cp++; |
| + break; |
| + default: |
| + if (!element_allowed) { |
| + return -1; |
| + } |
| + bool valid_enum = false; |
| + for (intptr_t i = 0; enums[i] != NULL; i++) { |
|
Cutch
2016/01/06 19:18:26
Maybe allow for enums to be NULL so that an arbitr
turnidge
2016/01/06 20:58:12
Done.
|
| + intptr_t len = strlen(enums[i]); |
| + if (strncmp(cp, enums[i], len) == 0) { |
| + element_count++; |
| + valid_enum = true; |
| + cp += len; |
| + element_allowed = false; // we need a comma first. |
| + break; |
| + } |
| + } |
| + if (!valid_enum) { |
| + return -1; |
| + } |
| + break; |
| + } |
| + } |
| + } |
| + |
| + const char* const* enums_; |
| +}; |
| + |
| + |
| typedef bool (*ServiceMethodEntry)(Thread* thread, JSONStream* js); |
| @@ -2191,6 +2291,77 @@ static bool GetCoverage(Thread* thread, JSONStream* js) { |
| } |
| +static const char* kCallSitesStr = "CallSites"; |
| +static const char* kCoverageStr = "Coverage"; |
| + |
| + |
| +static const char* const report_enum_names[] = { |
| + kCallSitesStr, |
| + kCoverageStr, |
| + NULL, |
| +}; |
| + |
| + |
| +static const MethodParameter* get_source_report_params[] = { |
| + ISOLATE_PARAMETER, |
| + new EnumListParameter("reports", true, report_enum_names), |
| + new IdParameter("scriptId", false), |
| + new UIntParameter("tokenPos", false), |
| + new UIntParameter("endTokenPos", false), |
| + NULL, |
| +}; |
| + |
| + |
| +static bool GetSourceReport(Thread* thread, JSONStream* js) { |
| + const char* reports_str = js->LookupParam("reports"); |
| + const char** reports = |
| + EnumListParameter::Parse(thread->zone(), reports_str, report_enum_names); |
| + intptr_t report_set = 0; |
| + while (*reports != NULL) { |
| + if (strcmp(*reports, kCallSitesStr) == 0) { |
| + report_set |= SourceReport::kCallSites; |
| + } else if (strcmp(*reports, kCoverageStr) == 0) { |
| + report_set |= SourceReport::kCoverage; |
| + } |
| + reports++; |
| + } |
| + |
| + Script& script = Script::Handle(); |
| + intptr_t start_pos = UIntParameter::Parse(js->LookupParam("tokenPos")); |
| + intptr_t end_pos = UIntParameter::Parse(js->LookupParam("endTokenPos")); |
| + |
| + if (js->HasParam("scriptId")) { |
| + // Get the target script. |
| + const char* script_id_param = js->LookupParam("scriptId"); |
| + const Object& obj = |
| + Object::Handle(LookupHeapObject(thread, script_id_param, NULL)); |
| + if (obj.raw() == Object::sentinel().raw() || !obj.IsScript()) { |
| + PrintInvalidParamError(js, "scriptId"); |
| + return true; |
| + } |
| + script ^= obj.raw(); |
| + } else { |
| + if (js->HasParam("tokenPos")) { |
| + js->PrintError( |
| + kInvalidParams, |
| + "%s: the 'tokenPos' parameter requires the 'scriptId' parameter", |
| + js->method()); |
| + return false; |
|
Cutch
2016/01/06 19:18:25
These should return true or else we will hit the U
turnidge
2016/01/06 20:58:13
Thanks. Done.
|
| + } |
| + if (js->HasParam("endTokenPos")) { |
| + js->PrintError( |
| + kInvalidParams, |
| + "%s: the 'endTokenPos' parameter requires the 'scriptId' parameter", |
| + js->method()); |
| + return false; |
| + } |
| + } |
| + SourceReport report(report_set); |
| + report.PrintJSON(js, script, start_pos, end_pos); |
| + return true; |
| +} |
| + |
| + |
| static const MethodParameter* get_call_site_data_params[] = { |
| ISOLATE_PARAMETER, |
| new IdParameter("targetId", false), |
| @@ -3608,6 +3779,8 @@ static const ServiceMethodDescriptor service_methods_[] = { |
| get_retained_size_params }, |
| { "_getRetainingPath", GetRetainingPath, |
| get_retaining_path_params }, |
| + { "_getSourceReport", GetSourceReport, |
| + get_source_report_params }, |
| { "getStack", GetStack, |
| get_stack_params }, |
| { "_getTagProfile", GetTagProfile, |