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 <memory> |
6 | |
7 | #include "platform/globals.h" |
8 | |
9 | #include "include/dart_tools_api.h" |
10 | #include "vm/dart_api_impl.h" |
11 | #include "vm/dart_entry.h" |
12 | #include "vm/debugger.h" |
13 | #include "vm/debugger_api_impl_test.h" |
14 | #include "vm/globals.h" |
15 | #include "vm/heap/safepoint.h" |
16 | #include "vm/message_handler.h" |
17 | #include "vm/object_id_ring.h" |
18 | #include "vm/os.h" |
19 | #include "vm/port.h" |
20 | #include "vm/profiler.h" |
21 | #include "vm/service.h" |
22 | #include "vm/unit_test.h" |
23 | |
24 | namespace dart { |
25 | |
26 | // This flag is used in the Service_Flags test below. |
27 | DEFINE_FLAG(bool, service_testing_flag, false, "Comment" ); |
28 | |
29 | #ifndef PRODUCT |
30 | |
31 | class ServiceTestMessageHandler : public MessageHandler { |
32 | public: |
33 | ServiceTestMessageHandler() : _msg(NULL) {} |
34 | |
35 | ~ServiceTestMessageHandler() { |
36 | PortMap::ClosePorts(this); |
37 | free(_msg); |
38 | } |
39 | |
40 | MessageStatus HandleMessage(std::unique_ptr<Message> message) { |
41 | if (_msg != NULL) { |
42 | free(_msg); |
43 | _msg = NULL; |
44 | } |
45 | |
46 | // Parse the message. |
47 | Object& response_obj = Object::Handle(); |
48 | if (message->IsRaw()) { |
49 | response_obj = message->raw_obj(); |
50 | } else { |
51 | Thread* thread = Thread::Current(); |
52 | MessageSnapshotReader reader(message.get(), thread); |
53 | response_obj = reader.ReadObject(); |
54 | } |
55 | if (response_obj.IsString()) { |
56 | String& response = String::Handle(); |
57 | response ^= response_obj.raw(); |
58 | _msg = Utils::StrDup(response.ToCString()); |
59 | } else { |
60 | ASSERT(response_obj.IsArray()); |
61 | Array& response_array = Array::Handle(); |
62 | response_array ^= response_obj.raw(); |
63 | ASSERT(response_array.Length() == 1); |
64 | ExternalTypedData& response = ExternalTypedData::Handle(); |
65 | response ^= response_array.At(0); |
66 | _msg = Utils::StrDup(reinterpret_cast<char*>(response.DataAddr(0))); |
67 | } |
68 | |
69 | return kOK; |
70 | } |
71 | |
72 | const char* msg() const { return _msg; } |
73 | |
74 | virtual Isolate* isolate() const { return Isolate::Current(); } |
75 | |
76 | private: |
77 | char* _msg; |
78 | }; |
79 | |
80 | static ArrayPtr Eval(Dart_Handle lib, const char* expr) { |
81 | const String& dummy_isolate_id = String::Handle(String::New("isolateId" )); |
82 | Dart_Handle expr_val; |
83 | { |
84 | TransitionVMToNative transiton(Thread::Current()); |
85 | expr_val = Dart_EvaluateStaticExpr(lib, NewString(expr)); |
86 | EXPECT_VALID(expr_val); |
87 | } |
88 | Zone* zone = Thread::Current()->zone(); |
89 | const GrowableObjectArray& value = |
90 | Api::UnwrapGrowableObjectArrayHandle(zone, expr_val); |
91 | const Array& result = Array::Handle(Array::MakeFixedLength(value)); |
92 | GrowableObjectArray& growable = GrowableObjectArray::Handle(); |
93 | growable ^= result.At(4); |
94 | // Append dummy isolate id to parameter values. |
95 | growable.Add(dummy_isolate_id); |
96 | Array& array = Array::Handle(Array::MakeFixedLength(growable)); |
97 | result.SetAt(4, array); |
98 | growable ^= result.At(5); |
99 | // Append dummy isolate id to parameter values. |
100 | growable.Add(dummy_isolate_id); |
101 | array = Array::MakeFixedLength(growable); |
102 | result.SetAt(5, array); |
103 | return result.raw(); |
104 | } |
105 | |
106 | static ArrayPtr EvalF(Dart_Handle lib, const char* fmt, ...) { |
107 | va_list args; |
108 | va_start(args, fmt); |
109 | intptr_t len = Utils::VSNPrint(NULL, 0, fmt, args); |
110 | va_end(args); |
111 | |
112 | char* buffer = Thread::Current()->zone()->Alloc<char>(len + 1); |
113 | va_list args2; |
114 | va_start(args2, fmt); |
115 | Utils::VSNPrint(buffer, (len + 1), fmt, args2); |
116 | va_end(args2); |
117 | |
118 | return Eval(lib, buffer); |
119 | } |
120 | |
121 | static FunctionPtr GetFunction(const Class& cls, const char* name) { |
122 | const Function& result = Function::Handle( |
123 | cls.LookupDynamicFunction(String::Handle(String::New(name)))); |
124 | EXPECT(!result.IsNull()); |
125 | return result.raw(); |
126 | } |
127 | |
128 | static ClassPtr GetClass(const Library& lib, const char* name) { |
129 | const Class& cls = Class::Handle( |
130 | lib.LookupClass(String::Handle(Symbols::New(Thread::Current(), name)))); |
131 | EXPECT(!cls.IsNull()); // No ambiguity error expected. |
132 | return cls.raw(); |
133 | } |
134 | |
135 | static void HandleIsolateMessage(Isolate* isolate, const Array& msg) { |
136 | Service::HandleIsolateMessage(isolate, msg); |
137 | } |
138 | |
139 | static void HandleRootMessage(const Array& message) { |
140 | Service::HandleRootMessage(message); |
141 | } |
142 | |
143 | ISOLATE_UNIT_TEST_CASE(Service_IsolateStickyError) { |
144 | const char* kScript = "main() => throw 'HI THERE STICKY';\n" ; |
145 | |
146 | Isolate* isolate = thread->isolate(); |
147 | isolate->set_is_runnable(true); |
148 | Dart_Handle result; |
149 | { |
150 | TransitionVMToNative transition(thread); |
151 | Dart_Handle lib = TestCase::LoadTestScript(kScript, NULL); |
152 | EXPECT_VALID(lib); |
153 | result = Dart_Invoke(lib, NewString("main" ), 0, NULL); |
154 | EXPECT(Dart_IsUnhandledExceptionError(result)); |
155 | EXPECT(!Dart_HasStickyError()); |
156 | } |
157 | EXPECT(Thread::Current()->sticky_error() == Error::null()); |
158 | |
159 | { |
160 | JSONStream js; |
161 | isolate->PrintJSON(&js, false); |
162 | // No error property and no PauseExit state. |
163 | EXPECT_NOTSUBSTRING("\"error\":" , js.ToCString()); |
164 | EXPECT_NOTSUBSTRING("HI THERE STICKY" , js.ToCString()); |
165 | EXPECT_NOTSUBSTRING("PauseExit" , js.ToCString()); |
166 | } |
167 | |
168 | { |
169 | // Set the sticky error. |
170 | TransitionVMToNative transition(thread); |
171 | Dart_SetStickyError(result); |
172 | Dart_SetPausedOnExit(true); |
173 | EXPECT(Dart_HasStickyError()); |
174 | } |
175 | |
176 | { |
177 | JSONStream js; |
178 | isolate->PrintJSON(&js, false); |
179 | // Error and PauseExit set. |
180 | EXPECT_SUBSTRING("\"error\":" , js.ToCString()); |
181 | EXPECT_SUBSTRING("HI THERE STICKY" , js.ToCString()); |
182 | EXPECT_SUBSTRING("PauseExit" , js.ToCString()); |
183 | } |
184 | } |
185 | |
186 | ISOLATE_UNIT_TEST_CASE(Service_IdZones) { |
187 | Zone* zone = thread->zone(); |
188 | Isolate* isolate = thread->isolate(); |
189 | ObjectIdRing* ring = isolate->EnsureObjectIdRing(); |
190 | |
191 | const String& test_a = String::Handle(zone, String::New("a" )); |
192 | const String& test_b = String::Handle(zone, String::New("b" )); |
193 | const String& test_c = String::Handle(zone, String::New("c" )); |
194 | const String& test_d = String::Handle(zone, String::New("d" )); |
195 | |
196 | // Both RingServiceIdZones share the same backing store and id space. |
197 | |
198 | // Always allocate a new id. |
199 | RingServiceIdZone always_new_zone; |
200 | always_new_zone.Init(ring, ObjectIdRing::kAllocateId); |
201 | EXPECT_STREQ("objects/0" , always_new_zone.GetServiceId(test_a)); |
202 | EXPECT_STREQ("objects/1" , always_new_zone.GetServiceId(test_a)); |
203 | EXPECT_STREQ("objects/2" , always_new_zone.GetServiceId(test_a)); |
204 | EXPECT_STREQ("objects/3" , always_new_zone.GetServiceId(test_b)); |
205 | EXPECT_STREQ("objects/4" , always_new_zone.GetServiceId(test_c)); |
206 | |
207 | // Reuse an existing id or allocate a new id. |
208 | RingServiceIdZone reuse_zone; |
209 | reuse_zone.Init(ring, ObjectIdRing::kReuseId); |
210 | EXPECT_STREQ("objects/0" , reuse_zone.GetServiceId(test_a)); |
211 | EXPECT_STREQ("objects/0" , reuse_zone.GetServiceId(test_a)); |
212 | EXPECT_STREQ("objects/3" , reuse_zone.GetServiceId(test_b)); |
213 | EXPECT_STREQ("objects/3" , reuse_zone.GetServiceId(test_b)); |
214 | EXPECT_STREQ("objects/4" , reuse_zone.GetServiceId(test_c)); |
215 | EXPECT_STREQ("objects/4" , reuse_zone.GetServiceId(test_c)); |
216 | EXPECT_STREQ("objects/5" , reuse_zone.GetServiceId(test_d)); |
217 | EXPECT_STREQ("objects/5" , reuse_zone.GetServiceId(test_d)); |
218 | } |
219 | |
220 | ISOLATE_UNIT_TEST_CASE(Service_Code) { |
221 | const char* kScript = |
222 | "var port;\n" // Set to our mock port by C++. |
223 | "\n" |
224 | "class A {\n" |
225 | " var a;\n" |
226 | " dynamic b() {}\n" |
227 | " dynamic c() {\n" |
228 | " var d = () { b(); };\n" |
229 | " return d;\n" |
230 | " }\n" |
231 | "}\n" |
232 | "main() {\n" |
233 | " var z = new A();\n" |
234 | " var x = z.c();\n" |
235 | " x();\n" |
236 | "}" ; |
237 | |
238 | Isolate* isolate = thread->isolate(); |
239 | isolate->set_is_runnable(true); |
240 | Dart_Handle lib; |
241 | Library& vmlib = Library::Handle(); |
242 | { |
243 | TransitionVMToNative transition(thread); |
244 | lib = TestCase::LoadTestScript(kScript, NULL); |
245 | EXPECT_VALID(lib); |
246 | EXPECT(!Dart_IsNull(lib)); |
247 | Dart_Handle result = Dart_Invoke(lib, NewString("main" ), 0, NULL); |
248 | EXPECT_VALID(result); |
249 | } |
250 | vmlib ^= Api::UnwrapHandle(lib); |
251 | EXPECT(!vmlib.IsNull()); |
252 | const Class& class_a = Class::Handle(GetClass(vmlib, "A" )); |
253 | EXPECT(!class_a.IsNull()); |
254 | const Function& function_c = Function::Handle(GetFunction(class_a, "c" )); |
255 | EXPECT(!function_c.IsNull()); |
256 | const Code& code_c = Code::Handle(function_c.CurrentCode()); |
257 | EXPECT(!code_c.IsNull()); |
258 | // Use the entry of the code object as it's reference. |
259 | uword entry = code_c.PayloadStart(); |
260 | int64_t compile_timestamp = code_c.compile_timestamp(); |
261 | EXPECT_GT(code_c.Size(), 16); |
262 | uword last = entry + code_c.Size(); |
263 | |
264 | // Build a mock message handler and wrap it in a dart port. |
265 | ServiceTestMessageHandler handler; |
266 | Dart_Port port_id = PortMap::CreatePort(&handler); |
267 | Dart_Handle port = Api::NewHandle(thread, SendPort::New(port_id)); |
268 | { |
269 | TransitionVMToNative transition(thread); |
270 | EXPECT_VALID(port); |
271 | EXPECT_VALID(Dart_SetField(lib, NewString("port" ), port)); |
272 | } |
273 | |
274 | Array& service_msg = Array::Handle(); |
275 | |
276 | // Request an invalid code object. |
277 | service_msg = |
278 | Eval(lib, "[0, port, '0', 'getObject', ['objectId'], ['code/0']]" ); |
279 | HandleIsolateMessage(isolate, service_msg); |
280 | EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
281 | EXPECT_SUBSTRING("\"error\"" , handler.msg()); |
282 | |
283 | // The following test checks that a code object can be found only |
284 | // at compile_timestamp()-code.EntryPoint(). |
285 | service_msg = EvalF(lib, |
286 | "[0, port, '0', 'getObject', " |
287 | "['objectId'], ['code/%" Px64 "-%" Px "']]" , |
288 | compile_timestamp, entry); |
289 | HandleIsolateMessage(isolate, service_msg); |
290 | EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
291 | EXPECT_SUBSTRING("\"type\":\"Code\"" , handler.msg()); |
292 | { |
293 | // Only perform a partial match. |
294 | const intptr_t kBufferSize = 512; |
295 | char buffer[kBufferSize]; |
296 | Utils::SNPrint(buffer, kBufferSize - 1, |
297 | "\"fixedId\":true,\"id\":\"code\\/%" Px64 "-%" Px "\"," , |
298 | compile_timestamp, entry); |
299 | EXPECT_SUBSTRING(buffer, handler.msg()); |
300 | } |
301 | |
302 | // Request code object at compile_timestamp-code.EntryPoint() + 16 |
303 | // Expect this to fail because the address is not the entry point. |
304 | uintptr_t address = entry + 16; |
305 | service_msg = EvalF(lib, |
306 | "[0, port, '0', 'getObject', " |
307 | "['objectId'], ['code/%" Px64 "-%" Px "']]" , |
308 | compile_timestamp, address); |
309 | HandleIsolateMessage(isolate, service_msg); |
310 | EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
311 | EXPECT_SUBSTRING("\"error\"" , handler.msg()); |
312 | |
313 | // Request code object at (compile_timestamp - 1)-code.EntryPoint() |
314 | // Expect this to fail because the timestamp is wrong. |
315 | address = entry; |
316 | service_msg = EvalF(lib, |
317 | "[0, port, '0', 'getObject', " |
318 | "['objectId'], ['code/%" Px64 "-%" Px "']]" , |
319 | compile_timestamp - 1, address); |
320 | HandleIsolateMessage(isolate, service_msg); |
321 | EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
322 | EXPECT_SUBSTRING("\"error\"" , handler.msg()); |
323 | |
324 | // Request native code at address. Expect the null code object back. |
325 | address = last; |
326 | service_msg = EvalF(lib, |
327 | "[0, port, '0', 'getObject', " |
328 | "['objectId'], ['code/native-%" Px "']]" , |
329 | address); |
330 | HandleIsolateMessage(isolate, service_msg); |
331 | EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
332 | // TODO(turnidge): It is pretty broken to return an Instance here. Fix. |
333 | EXPECT_SUBSTRING("\"kind\":\"Null\"" , handler.msg()); |
334 | |
335 | // Request malformed native code. |
336 | service_msg = EvalF(lib, |
337 | "[0, port, '0', 'getObject', ['objectId'], " |
338 | "['code/native%" Px "']]" , |
339 | address); |
340 | HandleIsolateMessage(isolate, service_msg); |
341 | EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
342 | EXPECT_SUBSTRING("\"error\"" , handler.msg()); |
343 | } |
344 | |
345 | ISOLATE_UNIT_TEST_CASE(Service_PcDescriptors) { |
346 | const char* kScript = |
347 | "var port;\n" // Set to our mock port by C++. |
348 | "\n" |
349 | "class A {\n" |
350 | " var a;\n" |
351 | " dynamic b() {}\n" |
352 | " dynamic c() {\n" |
353 | " var d = () { b(); };\n" |
354 | " return d;\n" |
355 | " }\n" |
356 | "}\n" |
357 | "main() {\n" |
358 | " var z = new A();\n" |
359 | " var x = z.c();\n" |
360 | " x();\n" |
361 | "}" ; |
362 | |
363 | Isolate* isolate = thread->isolate(); |
364 | isolate->set_is_runnable(true); |
365 | Dart_Handle lib; |
366 | Library& vmlib = Library::Handle(); |
367 | { |
368 | TransitionVMToNative transition(thread); |
369 | lib = TestCase::LoadTestScript(kScript, NULL); |
370 | EXPECT_VALID(lib); |
371 | EXPECT(!Dart_IsNull(lib)); |
372 | Dart_Handle result = Dart_Invoke(lib, NewString("main" ), 0, NULL); |
373 | EXPECT_VALID(result); |
374 | } |
375 | vmlib ^= Api::UnwrapHandle(lib); |
376 | EXPECT(!vmlib.IsNull()); |
377 | const Class& class_a = Class::Handle(GetClass(vmlib, "A" )); |
378 | EXPECT(!class_a.IsNull()); |
379 | const Function& function_c = Function::Handle(GetFunction(class_a, "c" )); |
380 | EXPECT(!function_c.IsNull()); |
381 | const Code& code_c = Code::Handle(function_c.CurrentCode()); |
382 | EXPECT(!code_c.IsNull()); |
383 | |
384 | const PcDescriptors& descriptors = |
385 | PcDescriptors::Handle(code_c.pc_descriptors()); |
386 | EXPECT(!descriptors.IsNull()); |
387 | ObjectIdRing* ring = isolate->EnsureObjectIdRing(); |
388 | intptr_t id = ring->GetIdForObject(descriptors.raw()); |
389 | |
390 | // Build a mock message handler and wrap it in a dart port. |
391 | ServiceTestMessageHandler handler; |
392 | Dart_Port port_id = PortMap::CreatePort(&handler); |
393 | Dart_Handle port = Api::NewHandle(thread, SendPort::New(port_id)); |
394 | { |
395 | TransitionVMToNative transition(thread); |
396 | EXPECT_VALID(port); |
397 | EXPECT_VALID(Dart_SetField(lib, NewString("port" ), port)); |
398 | } |
399 | |
400 | Array& service_msg = Array::Handle(); |
401 | |
402 | // Fetch object. |
403 | service_msg = EvalF(lib, |
404 | "[0, port, '0', 'getObject', " |
405 | "['objectId'], ['objects/%" Pd "']]" , |
406 | id); |
407 | HandleIsolateMessage(isolate, service_msg); |
408 | EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
409 | // Check type. |
410 | EXPECT_SUBSTRING("\"type\":\"Object\"" , handler.msg()); |
411 | EXPECT_SUBSTRING("\"_vmType\":\"PcDescriptors\"" , handler.msg()); |
412 | // Check for members array. |
413 | EXPECT_SUBSTRING("\"members\":[" , handler.msg()); |
414 | } |
415 | |
416 | ISOLATE_UNIT_TEST_CASE(Service_LocalVarDescriptors) { |
417 | const char* kScript = |
418 | "var port;\n" // Set to our mock port by C++. |
419 | "\n" |
420 | "class A {\n" |
421 | " var a;\n" |
422 | " dynamic b() {}\n" |
423 | " dynamic c() {\n" |
424 | " var d = () { b(); };\n" |
425 | " return d;\n" |
426 | " }\n" |
427 | "}\n" |
428 | "main() {\n" |
429 | " var z = new A();\n" |
430 | " var x = z.c();\n" |
431 | " x();\n" |
432 | "}" ; |
433 | |
434 | Isolate* isolate = thread->isolate(); |
435 | isolate->set_is_runnable(true); |
436 | Dart_Handle lib; |
437 | Library& vmlib = Library::Handle(); |
438 | { |
439 | TransitionVMToNative transition(thread); |
440 | lib = TestCase::LoadTestScript(kScript, NULL); |
441 | EXPECT_VALID(lib); |
442 | EXPECT(!Dart_IsNull(lib)); |
443 | Dart_Handle result = Dart_Invoke(lib, NewString("main" ), 0, NULL); |
444 | EXPECT_VALID(result); |
445 | } |
446 | vmlib ^= Api::UnwrapHandle(lib); |
447 | EXPECT(!vmlib.IsNull()); |
448 | const Class& class_a = Class::Handle(GetClass(vmlib, "A" )); |
449 | EXPECT(!class_a.IsNull()); |
450 | const Function& function_c = Function::Handle(GetFunction(class_a, "c" )); |
451 | EXPECT(!function_c.IsNull()); |
452 | const Code& code_c = Code::Handle(function_c.CurrentCode()); |
453 | EXPECT(!code_c.IsNull()); |
454 | |
455 | const LocalVarDescriptors& descriptors = |
456 | LocalVarDescriptors::Handle(code_c.GetLocalVarDescriptors()); |
457 | // Generate an ID for this object. |
458 | ObjectIdRing* ring = isolate->EnsureObjectIdRing(); |
459 | intptr_t id = ring->GetIdForObject(descriptors.raw()); |
460 | |
461 | // Build a mock message handler and wrap it in a dart port. |
462 | ServiceTestMessageHandler handler; |
463 | Dart_Port port_id = PortMap::CreatePort(&handler); |
464 | Dart_Handle port = Api::NewHandle(thread, SendPort::New(port_id)); |
465 | { |
466 | TransitionVMToNative transition(thread); |
467 | EXPECT_VALID(port); |
468 | EXPECT_VALID(Dart_SetField(lib, NewString("port" ), port)); |
469 | } |
470 | |
471 | Array& service_msg = Array::Handle(); |
472 | |
473 | // Fetch object. |
474 | service_msg = EvalF(lib, |
475 | "[0, port, '0', 'getObject', " |
476 | "['objectId'], ['objects/%" Pd "']]" , |
477 | id); |
478 | HandleIsolateMessage(isolate, service_msg); |
479 | EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
480 | // Check type. |
481 | EXPECT_SUBSTRING("\"type\":\"Object\"" , handler.msg()); |
482 | EXPECT_SUBSTRING("\"_vmType\":\"LocalVarDescriptors\"" , handler.msg()); |
483 | // Check for members array. |
484 | EXPECT_SUBSTRING("\"members\":[" , handler.msg()); |
485 | } |
486 | |
487 | static void WeakHandleFinalizer(void* isolate_callback_data, |
488 | Dart_WeakPersistentHandle handle, |
489 | void* peer) {} |
490 | |
491 | ISOLATE_UNIT_TEST_CASE(Service_PersistentHandles) { |
492 | const char* kScript = |
493 | "var port;\n" // Set to our mock port by C++. |
494 | "\n" |
495 | "class A {\n" |
496 | " var a;\n" |
497 | "}\n" |
498 | "var global = new A();\n" |
499 | "main() {\n" |
500 | " return global;\n" |
501 | "}" ; |
502 | |
503 | Isolate* isolate = thread->isolate(); |
504 | isolate->set_is_runnable(true); |
505 | |
506 | Dart_Handle lib; |
507 | Dart_PersistentHandle persistent_handle; |
508 | Dart_WeakPersistentHandle weak_persistent_handle; |
509 | { |
510 | TransitionVMToNative transition(thread); |
511 | lib = TestCase::LoadTestScript(kScript, NULL); |
512 | EXPECT_VALID(lib); |
513 | Dart_Handle result = Dart_Invoke(lib, NewString("main" ), 0, NULL); |
514 | EXPECT_VALID(result); |
515 | |
516 | // Create a persistent handle to global. |
517 | persistent_handle = Dart_NewPersistentHandle(result); |
518 | |
519 | // Create a weak persistent handle to global. |
520 | weak_persistent_handle = Dart_NewWeakPersistentHandle( |
521 | result, reinterpret_cast<void*>(0xdeadbeef), 128, WeakHandleFinalizer); |
522 | } |
523 | |
524 | // Build a mock message handler and wrap it in a dart port. |
525 | ServiceTestMessageHandler handler; |
526 | Dart_Port port_id = PortMap::CreatePort(&handler); |
527 | Dart_Handle port = Api::NewHandle(thread, SendPort::New(port_id)); |
528 | { |
529 | TransitionVMToNative transition(thread); |
530 | EXPECT_VALID(port); |
531 | EXPECT_VALID(Dart_SetField(lib, NewString("port" ), port)); |
532 | } |
533 | |
534 | Array& service_msg = Array::Handle(); |
535 | |
536 | // Get persistent handles. |
537 | service_msg = Eval(lib, "[0, port, '0', '_getPersistentHandles', [], []]" ); |
538 | HandleIsolateMessage(isolate, service_msg); |
539 | EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
540 | // Look for a heart beat. |
541 | EXPECT_SUBSTRING("\"type\":\"_PersistentHandles\"" , handler.msg()); |
542 | EXPECT_SUBSTRING("\"peer\":\"0xdeadbeef\"" , handler.msg()); |
543 | EXPECT_SUBSTRING("\"name\":\"A\"" , handler.msg()); |
544 | EXPECT_SUBSTRING("\"externalSize\":\"128\"" , handler.msg()); |
545 | |
546 | // Delete persistent handles. |
547 | { |
548 | TransitionVMToNative transition(thread); |
549 | Dart_DeletePersistentHandle(persistent_handle); |
550 | Dart_DeleteWeakPersistentHandle(weak_persistent_handle); |
551 | } |
552 | |
553 | // Get persistent handles (again). |
554 | service_msg = Eval(lib, "[0, port, '0', '_getPersistentHandles', [], []]" ); |
555 | HandleIsolateMessage(isolate, service_msg); |
556 | EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
557 | EXPECT_SUBSTRING("\"type\":\"_PersistentHandles\"" , handler.msg()); |
558 | // Verify that old persistent handles are not present. |
559 | EXPECT_NOTSUBSTRING("\"peer\":\"0xdeadbeef\"" , handler.msg()); |
560 | EXPECT_NOTSUBSTRING("\"name\":\"A\"" , handler.msg()); |
561 | EXPECT_NOTSUBSTRING("\"externalSize\":\"128\"" , handler.msg()); |
562 | } |
563 | |
564 | static bool alpha_callback(const char* name, |
565 | const char** option_keys, |
566 | const char** option_values, |
567 | intptr_t num_options, |
568 | void* user_data, |
569 | const char** result) { |
570 | *result = Utils::StrDup("alpha" ); |
571 | return true; |
572 | } |
573 | |
574 | static bool beta_callback(const char* name, |
575 | const char** option_keys, |
576 | const char** option_values, |
577 | intptr_t num_options, |
578 | void* user_data, |
579 | const char** result) { |
580 | *result = Utils::StrDup("beta" ); |
581 | return false; |
582 | } |
583 | |
584 | ISOLATE_UNIT_TEST_CASE(Service_EmbedderRootHandler) { |
585 | const char* kScript = |
586 | "var port;\n" // Set to our mock port by C++. |
587 | "\n" |
588 | "var x = 7;\n" |
589 | "main() {\n" |
590 | " x = x * x;\n" |
591 | " x = (x / 13).floor();\n" |
592 | "}" ; |
593 | |
594 | Dart_Handle lib; |
595 | { |
596 | TransitionVMToNative transition(thread); |
597 | |
598 | Dart_RegisterRootServiceRequestCallback("alpha" , alpha_callback, NULL); |
599 | Dart_RegisterRootServiceRequestCallback("beta" , beta_callback, NULL); |
600 | |
601 | lib = TestCase::LoadTestScript(kScript, NULL); |
602 | EXPECT_VALID(lib); |
603 | Dart_Handle result = Dart_Invoke(lib, NewString("main" ), 0, NULL); |
604 | EXPECT_VALID(result); |
605 | } |
606 | |
607 | // Build a mock message handler and wrap it in a dart port. |
608 | ServiceTestMessageHandler handler; |
609 | Dart_Port port_id = PortMap::CreatePort(&handler); |
610 | Dart_Handle port = Api::NewHandle(thread, SendPort::New(port_id)); |
611 | { |
612 | TransitionVMToNative transition(thread); |
613 | EXPECT_VALID(port); |
614 | EXPECT_VALID(Dart_SetField(lib, NewString("port" ), port)); |
615 | } |
616 | |
617 | Array& service_msg = Array::Handle(); |
618 | service_msg = Eval(lib, "[0, port, '\"', 'alpha', [], []]" ); |
619 | HandleRootMessage(service_msg); |
620 | EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
621 | EXPECT_STREQ("{\"jsonrpc\":\"2.0\", \"result\":alpha,\"id\":\"\\\"\"}" , |
622 | handler.msg()); |
623 | service_msg = Eval(lib, "[0, port, 1, 'beta', [], []]" ); |
624 | HandleRootMessage(service_msg); |
625 | EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
626 | EXPECT_STREQ("{\"jsonrpc\":\"2.0\", \"error\":beta,\"id\":1}" , handler.msg()); |
627 | } |
628 | |
629 | ISOLATE_UNIT_TEST_CASE(Service_EmbedderIsolateHandler) { |
630 | const char* kScript = |
631 | "var port;\n" // Set to our mock port by C++. |
632 | "\n" |
633 | "var x = 7;\n" |
634 | "main() {\n" |
635 | " x = x * x;\n" |
636 | " x = (x / 13).floor();\n" |
637 | "}" ; |
638 | |
639 | Dart_Handle lib; |
640 | { |
641 | TransitionVMToNative transition(thread); |
642 | |
643 | Dart_RegisterIsolateServiceRequestCallback("alpha" , alpha_callback, NULL); |
644 | Dart_RegisterIsolateServiceRequestCallback("beta" , beta_callback, NULL); |
645 | |
646 | lib = TestCase::LoadTestScript(kScript, NULL); |
647 | EXPECT_VALID(lib); |
648 | Dart_Handle result = Dart_Invoke(lib, NewString("main" ), 0, NULL); |
649 | EXPECT_VALID(result); |
650 | } |
651 | |
652 | // Build a mock message handler and wrap it in a dart port. |
653 | ServiceTestMessageHandler handler; |
654 | Dart_Port port_id = PortMap::CreatePort(&handler); |
655 | Dart_Handle port = Api::NewHandle(thread, SendPort::New(port_id)); |
656 | { |
657 | TransitionVMToNative transition(thread); |
658 | EXPECT_VALID(port); |
659 | EXPECT_VALID(Dart_SetField(lib, NewString("port" ), port)); |
660 | } |
661 | |
662 | Isolate* isolate = thread->isolate(); |
663 | Array& service_msg = Array::Handle(); |
664 | service_msg = Eval(lib, "[0, port, '0', 'alpha', [], []]" ); |
665 | HandleIsolateMessage(isolate, service_msg); |
666 | EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
667 | EXPECT_STREQ("{\"jsonrpc\":\"2.0\", \"result\":alpha,\"id\":\"0\"}" , |
668 | handler.msg()); |
669 | service_msg = Eval(lib, "[0, port, '0', 'beta', [], []]" ); |
670 | HandleIsolateMessage(isolate, service_msg); |
671 | EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
672 | EXPECT_STREQ("{\"jsonrpc\":\"2.0\", \"error\":beta,\"id\":\"0\"}" , |
673 | handler.msg()); |
674 | } |
675 | |
676 | // TODO(zra): Remove when tests are ready to enable. |
677 | #if !defined(TARGET_ARCH_ARM64) |
678 | |
679 | static void EnableProfiler() { |
680 | if (!FLAG_profiler) { |
681 | FLAG_profiler = true; |
682 | Profiler::Init(); |
683 | } |
684 | } |
685 | |
686 | ISOLATE_UNIT_TEST_CASE(Service_Profile) { |
687 | EnableProfiler(); |
688 | const char* kScript = |
689 | "var port;\n" // Set to our mock port by C++. |
690 | "\n" |
691 | "var x = 7;\n" |
692 | "main() {\n" |
693 | " x = x * x;\n" |
694 | " x = (x / 13).floor();\n" |
695 | "}" ; |
696 | |
697 | Isolate* isolate = thread->isolate(); |
698 | isolate->set_is_runnable(true); |
699 | Dart_Handle lib; |
700 | { |
701 | TransitionVMToNative transition(thread); |
702 | |
703 | lib = TestCase::LoadTestScript(kScript, NULL); |
704 | EXPECT_VALID(lib); |
705 | Dart_Handle result = Dart_Invoke(lib, NewString("main" ), 0, NULL); |
706 | EXPECT_VALID(result); |
707 | } |
708 | |
709 | // Build a mock message handler and wrap it in a dart port. |
710 | ServiceTestMessageHandler handler; |
711 | Dart_Port port_id = PortMap::CreatePort(&handler); |
712 | Dart_Handle port = Api::NewHandle(thread, SendPort::New(port_id)); |
713 | { |
714 | TransitionVMToNative transition(thread); |
715 | EXPECT_VALID(port); |
716 | EXPECT_VALID(Dart_SetField(lib, NewString("port" ), port)); |
717 | } |
718 | |
719 | Array& service_msg = Array::Handle(); |
720 | service_msg = Eval(lib, "[0, port, '0', 'getCpuSamples', [], []]" ); |
721 | HandleIsolateMessage(isolate, service_msg); |
722 | EXPECT_EQ(MessageHandler::kOK, handler.HandleNextMessage()); |
723 | // Expect profile |
724 | EXPECT_SUBSTRING("\"type\":\"CpuSamples\"" , handler.msg()); |
725 | } |
726 | |
727 | #endif // !defined(TARGET_ARCH_ARM64) |
728 | |
729 | #endif // !PRODUCT |
730 | |
731 | } // namespace dart |
732 | |