1 | // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "platform/assert.h" |
6 | |
7 | #include "include/dart_native_api.h" |
8 | #include "platform/unicode.h" |
9 | #include "vm/dart_entry.h" |
10 | #include "vm/debugger.h" |
11 | #include "vm/heap/safepoint.h" |
12 | #include "vm/json_stream.h" |
13 | #include "vm/message.h" |
14 | #include "vm/metrics.h" |
15 | #include "vm/object.h" |
16 | #include "vm/service.h" |
17 | #include "vm/service_event.h" |
18 | #include "vm/thread_registry.h" |
19 | #include "vm/timeline.h" |
20 | |
21 | namespace dart { |
22 | |
23 | #ifndef PRODUCT |
24 | |
25 | DECLARE_FLAG(bool, trace_service); |
26 | |
27 | JSONStream::JSONStream(intptr_t buf_size) |
28 | : writer_(buf_size), |
29 | default_id_zone_(), |
30 | id_zone_(&default_id_zone_), |
31 | reply_port_(ILLEGAL_PORT), |
32 | seq_(NULL), |
33 | parameter_keys_(NULL), |
34 | parameter_values_(NULL), |
35 | method_("" ), |
36 | param_keys_(NULL), |
37 | param_values_(NULL), |
38 | num_params_(0), |
39 | offset_(0), |
40 | count_(-1) { |
41 | ObjectIdRing* ring = NULL; |
42 | Isolate* isolate = Isolate::Current(); |
43 | if (isolate != NULL) { |
44 | ring = isolate->EnsureObjectIdRing(); |
45 | } |
46 | default_id_zone_.Init(ring, ObjectIdRing::kAllocateId); |
47 | } |
48 | |
49 | void JSONStream::Setup(Zone* zone, |
50 | Dart_Port reply_port, |
51 | const Instance& seq, |
52 | const String& method, |
53 | const Array& param_keys, |
54 | const Array& param_values, |
55 | bool parameters_are_dart_objects) { |
56 | set_reply_port(reply_port); |
57 | seq_ = &Instance::ZoneHandle(seq.raw()); |
58 | method_ = method.ToCString(); |
59 | |
60 | if (parameters_are_dart_objects) { |
61 | parameter_keys_ = &Array::ZoneHandle(param_keys.raw()); |
62 | parameter_values_ = &Array::ZoneHandle(param_values.raw()); |
63 | ASSERT(parameter_keys_->Length() == parameter_values_->Length()); |
64 | } else if (param_keys.Length() > 0) { |
65 | String& string_iterator = String::Handle(); |
66 | ASSERT(param_keys.Length() == param_values.Length()); |
67 | const char** param_keys_native = |
68 | zone->Alloc<const char*>(param_keys.Length()); |
69 | const char** param_values_native = |
70 | zone->Alloc<const char*>(param_keys.Length()); |
71 | for (intptr_t i = 0; i < param_keys.Length(); i++) { |
72 | string_iterator ^= param_keys.At(i); |
73 | param_keys_native[i] = |
74 | zone->MakeCopyOfString(string_iterator.ToCString()); |
75 | string_iterator ^= param_values.At(i); |
76 | param_values_native[i] = |
77 | zone->MakeCopyOfString(string_iterator.ToCString()); |
78 | } |
79 | SetParams(param_keys_native, param_values_native, param_keys.Length()); |
80 | } |
81 | |
82 | if (FLAG_trace_service) { |
83 | Isolate* isolate = Isolate::Current(); |
84 | ASSERT(isolate != NULL); |
85 | int64_t main_port = static_cast<int64_t>(isolate->main_port()); |
86 | const char* isolate_name = isolate->name(); |
87 | setup_time_micros_ = OS::GetCurrentTimeMicros(); |
88 | OS::PrintErr("[+%" Pd64 "ms] Isolate (%" Pd64 |
89 | ") %s processing service " |
90 | "request %s\n" , |
91 | Dart::UptimeMillis(), main_port, isolate_name, method_); |
92 | } |
93 | buffer()->Printf("{\"jsonrpc\":\"2.0\", \"result\":" ); |
94 | } |
95 | |
96 | void JSONStream::SetupError() { |
97 | Clear(); |
98 | buffer()->Printf("{\"jsonrpc\":\"2.0\", \"error\":" ); |
99 | } |
100 | |
101 | static const char* GetJSONRpcErrorMessage(intptr_t code) { |
102 | switch (code) { |
103 | case kParseError: |
104 | return "Parse error" ; |
105 | case kInvalidRequest: |
106 | return "Invalid Request" ; |
107 | case kMethodNotFound: |
108 | return "Method not found" ; |
109 | case kInvalidParams: |
110 | return "Invalid params" ; |
111 | case kInternalError: |
112 | return "Internal error" ; |
113 | case kFeatureDisabled: |
114 | return "Feature is disabled" ; |
115 | case kCannotAddBreakpoint: |
116 | return "Cannot add breakpoint" ; |
117 | case kStreamAlreadySubscribed: |
118 | return "Stream already subscribed" ; |
119 | case kStreamNotSubscribed: |
120 | return "Stream not subscribed" ; |
121 | case kIsolateMustBeRunnable: |
122 | return "Isolate must be runnable" ; |
123 | case kIsolateMustBePaused: |
124 | return "Isolate must be paused" ; |
125 | case kCannotResume: |
126 | return "Cannot resume execution" ; |
127 | case kIsolateIsReloading: |
128 | return "Isolate is reloading" ; |
129 | case kIsolateReloadBarred: |
130 | return "Isolate cannot be reloaded" ; |
131 | case kIsolateMustHaveReloaded: |
132 | return "Isolate must have reloaded" ; |
133 | case kFileSystemAlreadyExists: |
134 | return "File system already exists" ; |
135 | case kFileSystemDoesNotExist: |
136 | return "File system does not exist" ; |
137 | case kFileDoesNotExist: |
138 | return "File does not exist" ; |
139 | case kInvalidTimelineRequest: |
140 | return "The timeline related request could not be completed due to the " |
141 | "current configuration" ; |
142 | default: |
143 | return "Extension error" ; |
144 | } |
145 | } |
146 | |
147 | static void PrintRequest(JSONObject* obj, JSONStream* js) { |
148 | JSONObject jsobj(obj, "request" ); |
149 | jsobj.AddProperty("method" , js->method()); |
150 | { |
151 | JSONObject params(&jsobj, "params" ); |
152 | for (intptr_t i = 0; i < js->num_params(); i++) { |
153 | params.AddProperty(js->GetParamKey(i), js->GetParamValue(i)); |
154 | } |
155 | } |
156 | } |
157 | |
158 | void JSONStream::PrintError(intptr_t code, const char* details_format, ...) { |
159 | SetupError(); |
160 | JSONObject jsobj(this); |
161 | jsobj.AddProperty("code" , code); |
162 | jsobj.AddProperty("message" , GetJSONRpcErrorMessage(code)); |
163 | { |
164 | JSONObject data(&jsobj, "data" ); |
165 | PrintRequest(&data, this); |
166 | if (details_format != NULL) { |
167 | va_list args; |
168 | va_start(args, details_format); |
169 | intptr_t len = Utils::VSNPrint(NULL, 0, details_format, args); |
170 | va_end(args); |
171 | |
172 | char* buffer = Thread::Current()->zone()->Alloc<char>(len + 1); |
173 | va_list args2; |
174 | va_start(args2, details_format); |
175 | Utils::VSNPrint(buffer, (len + 1), details_format, args2); |
176 | va_end(args2); |
177 | data.AddProperty("details" , buffer); |
178 | } |
179 | } |
180 | } |
181 | |
182 | void JSONStream::PostNullReply(Dart_Port port) { |
183 | PortMap::PostMessage( |
184 | Message::New(port, Object::null(), Message::kNormalPriority)); |
185 | } |
186 | |
187 | static void Finalizer(void* isolate_callback_data, |
188 | Dart_WeakPersistentHandle handle, |
189 | void* buffer) { |
190 | free(buffer); |
191 | } |
192 | |
193 | void JSONStream::PostReply() { |
194 | ASSERT(seq_ != NULL); |
195 | Dart_Port port = reply_port(); |
196 | set_reply_port(ILLEGAL_PORT); // Prevent double replies. |
197 | if (seq_->IsString()) { |
198 | const String& str = String::Cast(*seq_); |
199 | PrintProperty("id" , str.ToCString()); |
200 | } else if (seq_->IsInteger()) { |
201 | const Integer& integer = Integer::Cast(*seq_); |
202 | PrintProperty64("id" , integer.AsInt64Value()); |
203 | } else if (seq_->IsDouble()) { |
204 | const Double& dbl = Double::Cast(*seq_); |
205 | PrintProperty("id" , dbl.value()); |
206 | } else if (seq_->IsNull()) { |
207 | if (port == ILLEGAL_PORT) { |
208 | // This path is only used in tests. |
209 | buffer()->AddChar('}'); // Finish our message. |
210 | char* cstr; |
211 | intptr_t length; |
212 | Steal(&cstr, &length); |
213 | OS::PrintErr("-----\nDropping reply:\n%s\n-----\n" , cstr); |
214 | free(cstr); |
215 | } |
216 | // JSON-RPC 2.0 says that a request with a null ID shouldn't get a reply. |
217 | PostNullReply(port); |
218 | return; |
219 | } |
220 | ASSERT(port != ILLEGAL_PORT); |
221 | |
222 | buffer()->AddChar('}'); // Finish our message. |
223 | char* cstr; |
224 | intptr_t length; |
225 | Steal(&cstr, &length); |
226 | |
227 | bool result; |
228 | { |
229 | TransitionVMToNative transition(Thread::Current()); |
230 | Dart_CObject bytes; |
231 | bytes.type = Dart_CObject_kExternalTypedData; |
232 | bytes.value.as_external_typed_data.type = Dart_TypedData_kUint8; |
233 | bytes.value.as_external_typed_data.length = length; |
234 | bytes.value.as_external_typed_data.data = reinterpret_cast<uint8_t*>(cstr); |
235 | bytes.value.as_external_typed_data.peer = cstr; |
236 | bytes.value.as_external_typed_data.callback = Finalizer; |
237 | Dart_CObject* elements[1]; |
238 | elements[0] = &bytes; |
239 | Dart_CObject message; |
240 | message.type = Dart_CObject_kArray; |
241 | message.value.as_array.length = 1; |
242 | message.value.as_array.values = elements; |
243 | result = Dart_PostCObject(port, &message); |
244 | } |
245 | |
246 | if (!result) { |
247 | free(cstr); |
248 | } |
249 | |
250 | if (FLAG_trace_service) { |
251 | Isolate* isolate = Isolate::Current(); |
252 | ASSERT(isolate != NULL); |
253 | int64_t main_port = static_cast<int64_t>(isolate->main_port()); |
254 | const char* isolate_name = isolate->name(); |
255 | int64_t total_time = OS::GetCurrentTimeMicros() - setup_time_micros_; |
256 | if (result) { |
257 | OS::PrintErr("[+%" Pd64 "ms] Isolate (%" Pd64 |
258 | ") %s processed service request %s (%" Pd64 "us)\n" , |
259 | Dart::UptimeMillis(), main_port, isolate_name, method_, |
260 | total_time); |
261 | } else { |
262 | OS::PrintErr("[+%" Pd64 "ms] Isolate (%" Pd64 |
263 | ") %s processed service request %s (%" Pd64 "us) FAILED\n" , |
264 | Dart::UptimeMillis(), main_port, isolate_name, method_, |
265 | total_time); |
266 | } |
267 | } |
268 | } |
269 | |
270 | const char* JSONStream::LookupParam(const char* key) const { |
271 | for (int i = 0; i < num_params(); i++) { |
272 | if (strcmp(key, param_keys_[i]) == 0) { |
273 | return param_values_[i]; |
274 | } |
275 | } |
276 | return NULL; |
277 | } |
278 | |
279 | bool JSONStream::HasParam(const char* key) const { |
280 | ASSERT(key); |
281 | return LookupParam(key) != NULL; |
282 | } |
283 | |
284 | bool JSONStream::ParamIs(const char* key, const char* value) const { |
285 | ASSERT(key); |
286 | ASSERT(value); |
287 | const char* key_value = LookupParam(key); |
288 | return (key_value != NULL) && (strcmp(key_value, value) == 0); |
289 | } |
290 | |
291 | void JSONStream::ComputeOffsetAndCount(intptr_t length, |
292 | intptr_t* offset, |
293 | intptr_t* count) { |
294 | // This function is written to avoid adding (count + offset) in case |
295 | // that triggers an integer overflow. |
296 | *offset = offset_; |
297 | if (*offset > length) { |
298 | *offset = length; |
299 | } |
300 | intptr_t remaining = length - *offset; |
301 | *count = count_; |
302 | if (*count < 0 || *count > remaining) { |
303 | *count = remaining; |
304 | } |
305 | } |
306 | void JSONStream::PrintfValue(const char* format, ...) { |
307 | va_list args; |
308 | va_start(args, format); |
309 | VPrintfValue(format, args); |
310 | va_end(args); |
311 | } |
312 | |
313 | void JSONStream::PrintValue(const Object& o, bool ref) { |
314 | PrintCommaIfNeeded(); |
315 | o.PrintJSON(this, ref); |
316 | } |
317 | |
318 | void JSONStream::PrintValue(Breakpoint* bpt) { |
319 | PrintCommaIfNeeded(); |
320 | bpt->PrintJSON(this); |
321 | } |
322 | |
323 | void JSONStream::PrintValue(TokenPosition tp) { |
324 | PrintCommaIfNeeded(); |
325 | PrintValue(tp.value()); |
326 | } |
327 | |
328 | void JSONStream::PrintValue(const ServiceEvent* event) { |
329 | PrintCommaIfNeeded(); |
330 | event->PrintJSON(this); |
331 | } |
332 | |
333 | void JSONStream::PrintValue(Metric* metric) { |
334 | PrintCommaIfNeeded(); |
335 | metric->PrintJSON(this); |
336 | } |
337 | |
338 | void JSONStream::PrintValue(MessageQueue* queue) { |
339 | PrintCommaIfNeeded(); |
340 | queue->PrintJSON(this); |
341 | } |
342 | |
343 | void JSONStream::PrintValue(Isolate* isolate, bool ref) { |
344 | PrintCommaIfNeeded(); |
345 | isolate->PrintJSON(this, ref); |
346 | } |
347 | |
348 | void JSONStream::PrintValue(IsolateGroup* isolate_group, bool ref) { |
349 | PrintCommaIfNeeded(); |
350 | isolate_group->PrintJSON(this, ref); |
351 | } |
352 | |
353 | void JSONStream::PrintValue(ThreadRegistry* reg) { |
354 | PrintCommaIfNeeded(); |
355 | reg->PrintJSON(this); |
356 | } |
357 | |
358 | void JSONStream::PrintValue(Thread* thread) { |
359 | PrintCommaIfNeeded(); |
360 | thread->PrintJSON(this); |
361 | } |
362 | |
363 | void JSONStream::PrintValue(const TimelineEvent* timeline_event) { |
364 | PrintCommaIfNeeded(); |
365 | timeline_event->PrintJSON(this); |
366 | } |
367 | |
368 | void JSONStream::PrintValue(const TimelineEventBlock* timeline_event_block) { |
369 | PrintCommaIfNeeded(); |
370 | timeline_event_block->PrintJSON(this); |
371 | } |
372 | |
373 | void JSONStream::PrintValueVM(bool ref) { |
374 | PrintCommaIfNeeded(); |
375 | Service::PrintJSONForVM(this, ref); |
376 | } |
377 | |
378 | void JSONStream::PrintServiceId(const Object& o) { |
379 | ASSERT(id_zone_ != NULL); |
380 | PrintProperty("id" , id_zone_->GetServiceId(o)); |
381 | } |
382 | |
383 | void JSONStream::PrintProperty(const char* name, const ServiceEvent* event) { |
384 | PrintPropertyName(name); |
385 | PrintValue(event); |
386 | } |
387 | |
388 | void JSONStream::PrintProperty(const char* name, Breakpoint* bpt) { |
389 | PrintPropertyName(name); |
390 | PrintValue(bpt); |
391 | } |
392 | |
393 | void JSONStream::PrintProperty(const char* name, TokenPosition tp) { |
394 | PrintPropertyName(name); |
395 | PrintValue(tp); |
396 | } |
397 | |
398 | void JSONStream::PrintProperty(const char* name, Metric* metric) { |
399 | PrintPropertyName(name); |
400 | PrintValue(metric); |
401 | } |
402 | |
403 | void JSONStream::PrintProperty(const char* name, MessageQueue* queue) { |
404 | PrintPropertyName(name); |
405 | PrintValue(queue); |
406 | } |
407 | |
408 | void JSONStream::PrintProperty(const char* name, Isolate* isolate) { |
409 | PrintPropertyName(name); |
410 | PrintValue(isolate); |
411 | } |
412 | |
413 | void JSONStream::PrintProperty(const char* name, ThreadRegistry* reg) { |
414 | PrintPropertyName(name); |
415 | PrintValue(reg); |
416 | } |
417 | |
418 | void JSONStream::PrintProperty(const char* name, Thread* thread) { |
419 | PrintPropertyName(name); |
420 | PrintValue(thread); |
421 | } |
422 | |
423 | void JSONStream::PrintProperty(const char* name, |
424 | const TimelineEvent* timeline_event) { |
425 | PrintPropertyName(name); |
426 | PrintValue(timeline_event); |
427 | } |
428 | |
429 | void JSONStream::PrintProperty(const char* name, |
430 | const TimelineEventBlock* timeline_event_block) { |
431 | PrintPropertyName(name); |
432 | PrintValue(timeline_event_block); |
433 | } |
434 | |
435 | void JSONStream::PrintfProperty(const char* name, const char* format, ...) { |
436 | va_list args; |
437 | va_start(args, format); |
438 | writer_.VPrintfProperty(name, format, args); |
439 | va_end(args); |
440 | } |
441 | |
442 | void JSONStream::set_reply_port(Dart_Port port) { |
443 | reply_port_ = port; |
444 | } |
445 | |
446 | intptr_t JSONStream::NumObjectParameters() const { |
447 | if (parameter_keys_ == NULL) { |
448 | return 0; |
449 | } |
450 | ASSERT(parameter_keys_ != NULL); |
451 | ASSERT(parameter_values_ != NULL); |
452 | return parameter_keys_->Length(); |
453 | } |
454 | |
455 | ObjectPtr JSONStream::GetObjectParameterKey(intptr_t i) const { |
456 | ASSERT((i >= 0) && (i < NumObjectParameters())); |
457 | return parameter_keys_->At(i); |
458 | } |
459 | |
460 | ObjectPtr JSONStream::GetObjectParameterValue(intptr_t i) const { |
461 | ASSERT((i >= 0) && (i < NumObjectParameters())); |
462 | return parameter_values_->At(i); |
463 | } |
464 | |
465 | ObjectPtr JSONStream::LookupObjectParam(const char* c_key) const { |
466 | const String& key = String::Handle(String::New(c_key)); |
467 | Object& test = Object::Handle(); |
468 | const intptr_t num_object_parameters = NumObjectParameters(); |
469 | for (intptr_t i = 0; i < num_object_parameters; i++) { |
470 | test = GetObjectParameterKey(i); |
471 | if (test.IsString() && String::Cast(test).Equals(key)) { |
472 | return GetObjectParameterValue(i); |
473 | } |
474 | } |
475 | return Object::null(); |
476 | } |
477 | |
478 | void JSONStream::SetParams(const char** param_keys, |
479 | const char** param_values, |
480 | intptr_t num_params) { |
481 | param_keys_ = param_keys; |
482 | param_values_ = param_values; |
483 | num_params_ = num_params; |
484 | } |
485 | |
486 | void JSONStream::PrintProperty(const char* name, const Object& o, bool ref) { |
487 | PrintPropertyName(name); |
488 | PrintValue(o, ref); |
489 | } |
490 | |
491 | void JSONStream::PrintPropertyVM(const char* name, bool ref) { |
492 | PrintPropertyName(name); |
493 | PrintValueVM(ref); |
494 | } |
495 | |
496 | JSONObject::JSONObject(const JSONArray* arr) : stream_(arr->stream_) { |
497 | stream_->OpenObject(); |
498 | } |
499 | |
500 | void JSONObject::AddFixedServiceId(const char* format, ...) const { |
501 | // Mark that this id is fixed. |
502 | AddProperty("fixedId" , true); |
503 | // Add the id property. |
504 | va_list args; |
505 | va_start(args, format); |
506 | stream_->VPrintfProperty("id" , format, args); |
507 | va_end(args); |
508 | } |
509 | |
510 | void JSONObject::AddServiceId(const char* format, ...) const { |
511 | // Add the id property. |
512 | va_list args; |
513 | va_start(args, format); |
514 | stream_->VPrintfProperty("id" , format, args); |
515 | va_end(args); |
516 | } |
517 | |
518 | void JSONObject::AddLocation(const Script& script, |
519 | TokenPosition token_pos, |
520 | TokenPosition end_token_pos) const { |
521 | JSONObject location(this, "location" ); |
522 | location.AddProperty("type" , "SourceLocation" ); |
523 | location.AddProperty("script" , script); |
524 | location.AddProperty("tokenPos" , token_pos); |
525 | if (end_token_pos.IsReal()) { |
526 | location.AddProperty("endTokenPos" , end_token_pos); |
527 | } |
528 | } |
529 | |
530 | void JSONObject::AddLocation(const BreakpointLocation* bpt_loc) const { |
531 | ASSERT(bpt_loc->IsResolved()); |
532 | |
533 | Zone* zone = Thread::Current()->zone(); |
534 | Script& script = Script::Handle(zone); |
535 | TokenPosition token_pos = TokenPosition::kNoSource; |
536 | bpt_loc->GetCodeLocation(&script, &token_pos); |
537 | AddLocation(script, token_pos); |
538 | } |
539 | |
540 | void JSONObject::AddUnresolvedLocation( |
541 | const BreakpointLocation* bpt_loc) const { |
542 | ASSERT(!bpt_loc->IsResolved()); |
543 | |
544 | Zone* zone = Thread::Current()->zone(); |
545 | Script& script = Script::Handle(zone); |
546 | TokenPosition token_pos = TokenPosition::kNoSource; |
547 | bpt_loc->GetCodeLocation(&script, &token_pos); |
548 | |
549 | JSONObject location(this, "location" ); |
550 | location.AddProperty("type" , "UnresolvedSourceLocation" ); |
551 | if (!script.IsNull()) { |
552 | location.AddProperty("script" , script); |
553 | } else { |
554 | const String& scriptUri = String::Handle(zone, bpt_loc->url()); |
555 | location.AddPropertyStr("scriptUri" , scriptUri); |
556 | } |
557 | if (bpt_loc->requested_line_number() >= 0) { |
558 | // This unresolved breakpoint was specified at a particular line. |
559 | location.AddProperty("line" , bpt_loc->requested_line_number()); |
560 | if (bpt_loc->requested_column_number() >= 0) { |
561 | location.AddProperty("column" , bpt_loc->requested_column_number()); |
562 | } |
563 | } else { |
564 | // This unresolved breakpoint was requested at some function entry. |
565 | location.AddProperty("tokenPos" , token_pos); |
566 | } |
567 | } |
568 | |
569 | void JSONObject::AddPropertyF(const char* name, const char* format, ...) const { |
570 | va_list args; |
571 | va_start(args, format); |
572 | stream_->VPrintfProperty(name, format, args); |
573 | va_end(args); |
574 | } |
575 | |
576 | void JSONArray::AddValueF(const char* format, ...) const { |
577 | va_list args; |
578 | va_start(args, format); |
579 | stream_->VPrintfValue(format, args); |
580 | va_end(args); |
581 | } |
582 | |
583 | #endif // !PRODUCT |
584 | |
585 | } // namespace dart |
586 | |