OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "vm/isolate.h" | 5 #include "vm/deferred_objects.h" |
6 | 6 |
7 #include "include/dart_api.h" | 7 #include "vm/deopt_instructions.h" |
8 #include "platform/assert.h" | 8 #include "vm/flags.h" |
9 #include "platform/json.h" | 9 #include "vm/object.h" |
10 #include "lib/mirrors.h" | |
11 #include "vm/code_observers.h" | |
12 #include "vm/compiler_stats.h" | |
13 #include "vm/coverage.h" | |
14 #include "vm/dart_api_state.h" | |
15 #include "vm/dart_entry.h" | |
16 #include "vm/debugger.h" | |
17 #include "vm/heap.h" | |
18 #include "vm/heap_histogram.h" | |
19 #include "vm/message_handler.h" | |
20 #include "vm/object_id_ring.h" | |
21 #include "vm/object_store.h" | |
22 #include "vm/parser.h" | |
23 #include "vm/port.h" | |
24 #include "vm/reusable_handles.h" | |
25 #include "vm/service.h" | |
26 #include "vm/simulator.h" | |
27 #include "vm/stack_frame.h" | |
28 #include "vm/stub_code.h" | |
29 #include "vm/symbols.h" | |
30 #include "vm/thread.h" | |
31 #include "vm/timer.h" | |
32 #include "vm/visitor.h" | |
33 | |
34 | 10 |
35 namespace dart { | 11 namespace dart { |
36 | 12 |
37 DEFINE_FLAG(bool, report_usage_count, false, | |
38 "Track function usage and report."); | |
39 DEFINE_FLAG(bool, trace_isolates, false, | |
40 "Trace isolate creation and shut down."); | |
41 DECLARE_FLAG(bool, trace_deoptimization_verbose); | 13 DECLARE_FLAG(bool, trace_deoptimization_verbose); |
42 | 14 |
43 | 15 |
44 void Isolate::RegisterClass(const Class& cls) { | |
45 class_table()->Register(cls); | |
46 if (object_histogram() != NULL) object_histogram()->RegisterClass(cls); | |
47 } | |
48 | |
49 | |
50 class IsolateMessageHandler : public MessageHandler { | |
51 public: | |
52 explicit IsolateMessageHandler(Isolate* isolate); | |
53 ~IsolateMessageHandler(); | |
54 | |
55 const char* name() const; | |
56 void MessageNotify(Message::Priority priority); | |
57 bool HandleMessage(Message* message); | |
58 | |
59 #if defined(DEBUG) | |
60 // Check that it is safe to access this handler. | |
61 void CheckAccess(); | |
62 #endif | |
63 bool IsCurrentIsolate() const; | |
64 virtual Isolate* GetIsolate() const { return isolate_; } | |
65 bool UnhandledExceptionCallbackHandler(const Object& message, | |
66 const UnhandledException& error); | |
67 | |
68 private: | |
69 bool ProcessUnhandledException(const Object& message, const Error& result); | |
70 RawFunction* ResolveCallbackFunction(); | |
71 Isolate* isolate_; | |
72 }; | |
73 | |
74 | |
75 IsolateMessageHandler::IsolateMessageHandler(Isolate* isolate) | |
76 : isolate_(isolate) { | |
77 } | |
78 | |
79 | |
80 IsolateMessageHandler::~IsolateMessageHandler() { | |
81 } | |
82 | |
83 const char* IsolateMessageHandler::name() const { | |
84 return isolate_->name(); | |
85 } | |
86 | |
87 | |
88 void IsolateMessageHandler::MessageNotify(Message::Priority priority) { | |
89 if (priority >= Message::kOOBPriority) { | |
90 // Handle out of band messages even if the isolate is busy. | |
91 isolate_->ScheduleInterrupts(Isolate::kMessageInterrupt); | |
92 } | |
93 Dart_MessageNotifyCallback callback = isolate_->message_notify_callback(); | |
94 if (callback) { | |
95 // Allow the embedder to handle message notification. | |
96 (*callback)(Api::CastIsolate(isolate_)); | |
97 } | |
98 } | |
99 | |
100 | |
101 bool IsolateMessageHandler::HandleMessage(Message* message) { | |
102 StartIsolateScope start_scope(isolate_); | |
103 StackZone zone(isolate_); | |
104 HandleScope handle_scope(isolate_); | |
105 | |
106 // If the message is in band we lookup the receive port to dispatch to. If | |
107 // the receive port is closed, we drop the message without deserializing it. | |
108 Object& receive_port = Object::Handle(); | |
109 if (!message->IsOOB()) { | |
110 receive_port = DartLibraryCalls::LookupReceivePort(message->dest_port()); | |
111 if (receive_port.IsError()) { | |
112 return ProcessUnhandledException(Object::null_instance(), | |
113 Error::Cast(receive_port)); | |
114 } | |
115 if (receive_port.IsNull()) { | |
116 delete message; | |
117 return true; | |
118 } | |
119 } | |
120 | |
121 // Parse the message. | |
122 SnapshotReader reader(message->data(), message->len(), | |
123 Snapshot::kMessage, Isolate::Current()); | |
124 const Object& msg_obj = Object::Handle(reader.ReadObject()); | |
125 if (msg_obj.IsError()) { | |
126 // An error occurred while reading the message. | |
127 return ProcessUnhandledException(Object::null_instance(), | |
128 Error::Cast(msg_obj)); | |
129 } | |
130 if (!msg_obj.IsNull() && !msg_obj.IsInstance()) { | |
131 // TODO(turnidge): We need to decide what an isolate does with | |
132 // malformed messages. If they (eventually) come from a remote | |
133 // machine, then it might make sense to drop the message entirely. | |
134 // In the case that the message originated locally, which is | |
135 // always true for now, then this should never occur. | |
136 UNREACHABLE(); | |
137 } | |
138 | |
139 Instance& msg = Instance::Handle(); | |
140 msg ^= msg_obj.raw(); // Can't use Instance::Cast because may be null. | |
141 | |
142 bool success = true; | |
143 if (message->IsOOB()) { | |
144 Service::HandleServiceMessage(isolate_, message->reply_port(), msg); | |
145 } else { | |
146 const Object& result = Object::Handle( | |
147 DartLibraryCalls::HandleMessage( | |
148 receive_port, message->reply_port(), msg)); | |
149 if (result.IsError()) { | |
150 success = ProcessUnhandledException(msg, Error::Cast(result)); | |
151 } else { | |
152 ASSERT(result.IsNull()); | |
153 } | |
154 } | |
155 delete message; | |
156 return success; | |
157 } | |
158 | |
159 | |
160 RawFunction* IsolateMessageHandler::ResolveCallbackFunction() { | |
161 ASSERT(isolate_->object_store()->unhandled_exception_handler() != NULL); | |
162 String& callback_name = String::Handle(isolate_); | |
163 if (isolate_->object_store()->unhandled_exception_handler() != | |
164 String::null()) { | |
165 callback_name = isolate_->object_store()->unhandled_exception_handler(); | |
166 } else { | |
167 callback_name = String::New("_unhandledExceptionCallback"); | |
168 } | |
169 Library& lib = | |
170 Library::Handle(isolate_, isolate_->object_store()->isolate_library()); | |
171 Function& func = | |
172 Function::Handle(isolate_, lib.LookupLocalFunction(callback_name)); | |
173 if (func.IsNull()) { | |
174 lib = isolate_->object_store()->root_library(); | |
175 func = lib.LookupLocalFunction(callback_name); | |
176 } | |
177 return func.raw(); | |
178 } | |
179 | |
180 | |
181 bool IsolateMessageHandler::UnhandledExceptionCallbackHandler( | |
182 const Object& message, const UnhandledException& error) { | |
183 const Instance& cause = Instance::Handle(isolate_, error.exception()); | |
184 const Instance& stacktrace = | |
185 Instance::Handle(isolate_, error.stacktrace()); | |
186 | |
187 // Wrap these args into an IsolateUncaughtException object. | |
188 const Array& exception_args = Array::Handle(Array::New(3)); | |
189 exception_args.SetAt(0, message); | |
190 exception_args.SetAt(1, cause); | |
191 exception_args.SetAt(2, stacktrace); | |
192 const Object& exception = | |
193 Object::Handle(isolate_, | |
194 Exceptions::Create(Exceptions::kIsolateUnhandledException, | |
195 exception_args)); | |
196 if (exception.IsError()) { | |
197 return false; | |
198 } | |
199 ASSERT(exception.IsInstance()); | |
200 | |
201 // Invoke script's callback function. | |
202 Object& function = Object::Handle(isolate_, ResolveCallbackFunction()); | |
203 if (function.IsNull() || function.IsError()) { | |
204 return false; | |
205 } | |
206 const Array& callback_args = Array::Handle(Array::New(1)); | |
207 callback_args.SetAt(0, exception); | |
208 const Object& result = | |
209 Object::Handle(DartEntry::InvokeFunction(Function::Cast(function), | |
210 callback_args)); | |
211 if (result.IsError()) { | |
212 const Error& err = Error::Cast(result); | |
213 OS::PrintErr("failed calling unhandled exception callback: %s\n", | |
214 err.ToErrorCString()); | |
215 return false; | |
216 } | |
217 | |
218 ASSERT(result.IsBool()); | |
219 bool continue_from_exception = Bool::Cast(result).value(); | |
220 if (continue_from_exception) { | |
221 isolate_->object_store()->clear_sticky_error(); | |
222 } | |
223 return continue_from_exception; | |
224 } | |
225 | |
226 #if defined(DEBUG) | |
227 void IsolateMessageHandler::CheckAccess() { | |
228 ASSERT(IsCurrentIsolate()); | |
229 } | |
230 #endif | |
231 | |
232 | |
233 bool IsolateMessageHandler::IsCurrentIsolate() const { | |
234 return (isolate_ == Isolate::Current()); | |
235 } | |
236 | |
237 | |
238 bool IsolateMessageHandler::ProcessUnhandledException( | |
239 const Object& message, const Error& result) { | |
240 if (result.IsUnhandledException()) { | |
241 // Invoke the isolate's uncaught exception handler, if it exists. | |
242 const UnhandledException& error = UnhandledException::Cast(result); | |
243 RawInstance* exception = error.exception(); | |
244 if ((exception != isolate_->object_store()->out_of_memory()) && | |
245 (exception != isolate_->object_store()->stack_overflow())) { | |
246 if (UnhandledExceptionCallbackHandler(message, error)) { | |
247 return true; | |
248 } | |
249 } | |
250 } | |
251 | |
252 // Invoke the isolate's unhandled exception callback if there is one. | |
253 if (Isolate::UnhandledExceptionCallback() != NULL) { | |
254 Dart_EnterScope(); | |
255 Dart_Handle error = Api::NewHandle(isolate_, result.raw()); | |
256 (Isolate::UnhandledExceptionCallback())(error); | |
257 Dart_ExitScope(); | |
258 } | |
259 | |
260 isolate_->object_store()->set_sticky_error(result); | |
261 return false; | |
262 } | |
263 | |
264 | |
265 #if defined(DEBUG) | |
266 // static | |
267 void BaseIsolate::AssertCurrent(BaseIsolate* isolate) { | |
268 ASSERT(isolate == Isolate::Current()); | |
269 } | |
270 #endif | |
271 | |
272 | |
273 void DeferredDouble::Materialize() { | 16 void DeferredDouble::Materialize() { |
274 RawDouble** double_slot = reinterpret_cast<RawDouble**>(slot()); | 17 RawDouble** double_slot = reinterpret_cast<RawDouble**>(slot()); |
275 *double_slot = Double::New(value()); | 18 *double_slot = Double::New(value()); |
276 | 19 |
277 if (FLAG_trace_deoptimization_verbose) { | 20 if (FLAG_trace_deoptimization_verbose) { |
278 OS::PrintErr("materializing double at %" Px ": %g\n", | 21 OS::PrintErr("materializing double at %" Px ": %g\n", |
279 reinterpret_cast<uword>(slot()), value()); | 22 reinterpret_cast<uword>(slot()), value()); |
280 } | 23 } |
281 } | 24 } |
282 | 25 |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
321 uint32_t y = raw_uint32x4->y(); | 64 uint32_t y = raw_uint32x4->y(); |
322 uint32_t z = raw_uint32x4->z(); | 65 uint32_t z = raw_uint32x4->z(); |
323 uint32_t w = raw_uint32x4->w(); | 66 uint32_t w = raw_uint32x4->w(); |
324 OS::PrintErr("materializing Uint32x4 at %" Px ": %x,%x,%x,%x\n", | 67 OS::PrintErr("materializing Uint32x4 at %" Px ": %x,%x,%x,%x\n", |
325 reinterpret_cast<uword>(slot()), x, y, z, w); | 68 reinterpret_cast<uword>(slot()), x, y, z, w); |
326 } | 69 } |
327 } | 70 } |
328 | 71 |
329 | 72 |
330 void DeferredObjectRef::Materialize() { | 73 void DeferredObjectRef::Materialize() { |
331 DeferredObject* obj = Isolate::Current()->GetDeferredObject(index()); | 74 // TODO(turnidge): Consider passing the deopt_context to materialize |
| 75 // instead of accessing it through the current isolate. It would |
| 76 // make it easier to test deferred object materialization in a unit |
| 77 // test eventually. |
| 78 DeferredObject* obj = |
| 79 Isolate::Current()->deopt_context()->GetDeferredObject(index()); |
332 *slot() = obj->object(); | 80 *slot() = obj->object(); |
333 if (FLAG_trace_deoptimization_verbose) { | 81 if (FLAG_trace_deoptimization_verbose) { |
334 OS::PrintErr("writing instance ref at %" Px ": %s\n", | 82 OS::PrintErr("writing instance ref at %" Px ": %s\n", |
335 reinterpret_cast<uword>(slot()), | 83 reinterpret_cast<uword>(slot()), |
336 Instance::Handle(obj->object()).ToCString()); | 84 Instance::Handle(obj->object()).ToCString()); |
337 } | 85 } |
338 } | 86 } |
339 | 87 |
340 | 88 |
341 RawInstance* DeferredObject::object() { | 89 RawInstance* DeferredObject::object() { |
(...skipping 27 matching lines...) Expand all Loading... |
369 if (FLAG_trace_deoptimization_verbose) { | 117 if (FLAG_trace_deoptimization_verbose) { |
370 OS::PrintErr(" %s <- %s\n", | 118 OS::PrintErr(" %s <- %s\n", |
371 String::Handle(field.name()).ToCString(), | 119 String::Handle(field.name()).ToCString(), |
372 value.ToCString()); | 120 value.ToCString()); |
373 } | 121 } |
374 } | 122 } |
375 | 123 |
376 object_ = &obj; | 124 object_ = &obj; |
377 } | 125 } |
378 | 126 |
379 | |
380 #define REUSABLE_HANDLE_INITIALIZERS(object) \ | |
381 object##_handle_(NULL), | |
382 | |
383 Isolate::Isolate() | |
384 : store_buffer_(), | |
385 message_notify_callback_(NULL), | |
386 name_(NULL), | |
387 start_time_(OS::GetCurrentTimeMicros()), | |
388 main_port_(0), | |
389 heap_(NULL), | |
390 object_store_(NULL), | |
391 top_context_(Context::null()), | |
392 top_exit_frame_info_(0), | |
393 init_callback_data_(NULL), | |
394 library_tag_handler_(NULL), | |
395 api_state_(NULL), | |
396 stub_code_(NULL), | |
397 debugger_(NULL), | |
398 single_step_(false), | |
399 simulator_(NULL), | |
400 long_jump_base_(NULL), | |
401 timer_list_(), | |
402 deopt_id_(0), | |
403 mutex_(new Mutex()), | |
404 stack_limit_(0), | |
405 saved_stack_limit_(0), | |
406 message_handler_(NULL), | |
407 spawn_data_(0), | |
408 is_runnable_(false), | |
409 gc_prologue_callbacks_(), | |
410 gc_epilogue_callbacks_(), | |
411 defer_finalization_count_(0), | |
412 deopt_cpu_registers_copy_(NULL), | |
413 deopt_fpu_registers_copy_(NULL), | |
414 deopt_frame_copy_(NULL), | |
415 deopt_frame_copy_size_(0), | |
416 deferred_boxes_(NULL), | |
417 deferred_object_refs_(NULL), | |
418 deferred_objects_count_(0), | |
419 deferred_objects_(NULL), | |
420 stacktrace_(NULL), | |
421 stack_frame_index_(-1), | |
422 object_histogram_(NULL), | |
423 object_id_ring_(NULL), | |
424 REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_INITIALIZERS) | |
425 reusable_handles_() { | |
426 if (FLAG_print_object_histogram && (Dart::vm_isolate() != NULL)) { | |
427 object_histogram_ = new ObjectHistogram(this); | |
428 } | |
429 } | |
430 #undef REUSABLE_HANDLE_INITIALIZERS | |
431 | |
432 | |
433 Isolate::~Isolate() { | |
434 delete [] name_; | |
435 delete heap_; | |
436 delete object_store_; | |
437 delete api_state_; | |
438 delete stub_code_; | |
439 delete debugger_; | |
440 #if defined(USING_SIMULATOR) | |
441 delete simulator_; | |
442 #endif | |
443 delete mutex_; | |
444 mutex_ = NULL; // Fail fast if interrupts are scheduled on a dead isolate. | |
445 delete message_handler_; | |
446 message_handler_ = NULL; // Fail fast if we send messages to a dead isolate. | |
447 delete object_histogram_; | |
448 } | |
449 | |
450 void Isolate::SetCurrent(Isolate* current) { | |
451 Thread::SetThreadLocal(isolate_key, reinterpret_cast<uword>(current)); | |
452 } | |
453 | |
454 | |
455 // The single thread local key which stores all the thread local data | |
456 // for a thread. Since an Isolate is the central repository for | |
457 // storing all isolate specific information a single thread local key | |
458 // is sufficient. | |
459 ThreadLocalKey Isolate::isolate_key = Thread::kUnsetThreadLocalKey; | |
460 | |
461 | |
462 void Isolate::InitOnce() { | |
463 ASSERT(isolate_key == Thread::kUnsetThreadLocalKey); | |
464 isolate_key = Thread::CreateThreadLocal(); | |
465 ASSERT(isolate_key != Thread::kUnsetThreadLocalKey); | |
466 create_callback_ = NULL; | |
467 } | |
468 | |
469 | |
470 Isolate* Isolate::Init(const char* name_prefix) { | |
471 Isolate* result = new Isolate(); | |
472 ASSERT(result != NULL); | |
473 | |
474 // TODO(5411455): For now just set the recently created isolate as | |
475 // the current isolate. | |
476 SetCurrent(result); | |
477 | |
478 // Setup the isolate specific resuable handles. | |
479 #define REUSABLE_HANDLE_ALLOCATION(object) \ | |
480 result->object##_handle_ = result->AllocateReusableHandle<object>(); \ | |
481 | |
482 REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_ALLOCATION) | |
483 #undef REUSABLE_HANDLE_ALLOCATION | |
484 | |
485 // Setup the isolate message handler. | |
486 MessageHandler* handler = new IsolateMessageHandler(result); | |
487 ASSERT(handler != NULL); | |
488 result->set_message_handler(handler); | |
489 | |
490 // Setup the Dart API state. | |
491 ApiState* state = new ApiState(); | |
492 ASSERT(state != NULL); | |
493 result->set_api_state(state); | |
494 | |
495 // Initialize stack top and limit in case we are running the isolate in the | |
496 // main thread. | |
497 // TODO(5411455): Need to figure out how to set the stack limit for the | |
498 // main thread. | |
499 result->SetStackLimitFromCurrentTOS(reinterpret_cast<uword>(&result)); | |
500 result->set_main_port(PortMap::CreatePort(result->message_handler())); | |
501 result->BuildName(name_prefix); | |
502 | |
503 result->debugger_ = new Debugger(); | |
504 result->debugger_->Initialize(result); | |
505 if (FLAG_trace_isolates) { | |
506 if (name_prefix == NULL || strcmp(name_prefix, "vm-isolate") != 0) { | |
507 OS::Print("[+] Starting isolate:\n" | |
508 "\tisolate: %s\n", result->name()); | |
509 } | |
510 } | |
511 return result; | |
512 } | |
513 | |
514 | |
515 void Isolate::BuildName(const char* name_prefix) { | |
516 ASSERT(name_ == NULL); | |
517 if (name_prefix == NULL) { | |
518 name_prefix = "isolate"; | |
519 } | |
520 const char* kFormat = "%s-%lld"; | |
521 intptr_t len = OS::SNPrint(NULL, 0, kFormat, name_prefix, main_port()) + 1; | |
522 name_ = new char[len]; | |
523 OS::SNPrint(name_, len, kFormat, name_prefix, main_port()); | |
524 } | |
525 | |
526 | |
527 // TODO(5411455): Use flag to override default value and Validate the | |
528 // stack size by querying OS. | |
529 uword Isolate::GetSpecifiedStackSize() { | |
530 ASSERT(Isolate::kStackSizeBuffer < Thread::GetMaxStackSize()); | |
531 uword stack_size = Thread::GetMaxStackSize() - Isolate::kStackSizeBuffer; | |
532 return stack_size; | |
533 } | |
534 | |
535 | |
536 void Isolate::SetStackLimitFromCurrentTOS(uword stack_top_value) { | |
537 #if defined(USING_SIMULATOR) | |
538 // Ignore passed-in native stack top and use Simulator stack top. | |
539 Simulator* sim = Simulator::Current(); // May allocate a simulator. | |
540 ASSERT(simulator() == sim); // This isolate's simulator is the current one. | |
541 stack_top_value = sim->StackTop(); | |
542 // The overflow area is accounted for by the simulator. | |
543 #endif | |
544 SetStackLimit(stack_top_value - GetSpecifiedStackSize()); | |
545 } | |
546 | |
547 | |
548 void Isolate::SetStackLimit(uword limit) { | |
549 MutexLocker ml(mutex_); | |
550 if (stack_limit_ == saved_stack_limit_) { | |
551 // No interrupt pending, set stack_limit_ too. | |
552 stack_limit_ = limit; | |
553 } | |
554 saved_stack_limit_ = limit; | |
555 } | |
556 | |
557 | |
558 void Isolate::ScheduleInterrupts(uword interrupt_bits) { | |
559 // TODO(turnidge): Can't use MutexLocker here because MutexLocker is | |
560 // a StackResource, which requires a current isolate. Should | |
561 // MutexLocker really be a StackResource? | |
562 mutex_->Lock(); | |
563 ASSERT((interrupt_bits & ~kInterruptsMask) == 0); // Must fit in mask. | |
564 if (stack_limit_ == saved_stack_limit_) { | |
565 stack_limit_ = (~static_cast<uword>(0)) & ~kInterruptsMask; | |
566 } | |
567 stack_limit_ |= interrupt_bits; | |
568 mutex_->Unlock(); | |
569 } | |
570 | |
571 | |
572 bool Isolate::MakeRunnable() { | |
573 ASSERT(Isolate::Current() == NULL); | |
574 // Can't use MutexLocker here because MutexLocker is | |
575 // a StackResource, which requires a current isolate. | |
576 mutex_->Lock(); | |
577 // Check if we are in a valid state to make the isolate runnable. | |
578 if (is_runnable_ == true) { | |
579 mutex_->Unlock(); | |
580 return false; // Already runnable. | |
581 } | |
582 // Set the isolate as runnable and if we are being spawned schedule | |
583 // isolate on thread pool for execution. | |
584 is_runnable_ = true; | |
585 IsolateSpawnState* state = reinterpret_cast<IsolateSpawnState*>(spawn_data()); | |
586 if (state != NULL) { | |
587 ASSERT(this == state->isolate()); | |
588 Run(); | |
589 } | |
590 mutex_->Unlock(); | |
591 return true; | |
592 } | |
593 | |
594 | |
595 static void StoreError(Isolate* isolate, const Object& obj) { | |
596 ASSERT(obj.IsError()); | |
597 isolate->object_store()->set_sticky_error(Error::Cast(obj)); | |
598 } | |
599 | |
600 | |
601 static bool RunIsolate(uword parameter) { | |
602 Isolate* isolate = reinterpret_cast<Isolate*>(parameter); | |
603 IsolateSpawnState* state = NULL; | |
604 { | |
605 MutexLocker ml(isolate->mutex()); | |
606 state = reinterpret_cast<IsolateSpawnState*>(isolate->spawn_data()); | |
607 isolate->set_spawn_data(0); | |
608 } | |
609 { | |
610 StartIsolateScope start_scope(isolate); | |
611 StackZone zone(isolate); | |
612 HandleScope handle_scope(isolate); | |
613 if (!ClassFinalizer::FinalizePendingClasses()) { | |
614 // Error is in sticky error already. | |
615 return false; | |
616 } | |
617 | |
618 // Set up specific unhandled exception handler. | |
619 const String& callback_name = String::Handle( | |
620 isolate, String::New(state->exception_callback_name())); | |
621 isolate->object_store()-> | |
622 set_unhandled_exception_handler(callback_name); | |
623 | |
624 Object& result = Object::Handle(); | |
625 result = state->ResolveFunction(); | |
626 delete state; | |
627 state = NULL; | |
628 if (result.IsError()) { | |
629 StoreError(isolate, result); | |
630 return false; | |
631 } | |
632 ASSERT(result.IsFunction()); | |
633 Function& func = Function::Handle(isolate); | |
634 func ^= result.raw(); | |
635 result = DartEntry::InvokeFunction(func, Object::empty_array()); | |
636 if (result.IsError()) { | |
637 StoreError(isolate, result); | |
638 return false; | |
639 } | |
640 } | |
641 return true; | |
642 } | |
643 | |
644 | |
645 static void ShutdownIsolate(uword parameter) { | |
646 Isolate* isolate = reinterpret_cast<Isolate*>(parameter); | |
647 { | |
648 // Print the error if there is one. This may execute dart code to | |
649 // print the exception object, so we need to use a StartIsolateScope. | |
650 StartIsolateScope start_scope(isolate); | |
651 StackZone zone(isolate); | |
652 HandleScope handle_scope(isolate); | |
653 Error& error = Error::Handle(); | |
654 error = isolate->object_store()->sticky_error(); | |
655 if (!error.IsNull()) { | |
656 OS::PrintErr("in ShutdownIsolate: %s\n", error.ToErrorCString()); | |
657 } | |
658 Dart::RunShutdownCallback(); | |
659 } | |
660 { | |
661 // Shut the isolate down. | |
662 SwitchIsolateScope switch_scope(isolate); | |
663 Dart::ShutdownIsolate(); | |
664 } | |
665 } | |
666 | |
667 | |
668 void Isolate::Run() { | |
669 message_handler()->Run(Dart::thread_pool(), | |
670 RunIsolate, | |
671 ShutdownIsolate, | |
672 reinterpret_cast<uword>(this)); | |
673 } | |
674 | |
675 | |
676 uword Isolate::GetAndClearInterrupts() { | |
677 MutexLocker ml(mutex_); | |
678 if (stack_limit_ == saved_stack_limit_) { | |
679 return 0; // No interrupt was requested. | |
680 } | |
681 uword interrupt_bits = stack_limit_ & kInterruptsMask; | |
682 stack_limit_ = saved_stack_limit_; | |
683 return interrupt_bits; | |
684 } | |
685 | |
686 | |
687 static int MostUsedFunctionFirst(const Function* const* a, | |
688 const Function* const* b) { | |
689 if ((*a)->usage_counter() > (*b)->usage_counter()) { | |
690 return -1; | |
691 } else if ((*a)->usage_counter() < (*b)->usage_counter()) { | |
692 return 1; | |
693 } else { | |
694 return 0; | |
695 } | |
696 } | |
697 | |
698 | |
699 static void AddFunctionsFromClass(const Class& cls, | |
700 GrowableArray<const Function*>* functions) { | |
701 const Array& class_functions = Array::Handle(cls.functions()); | |
702 // Class 'dynamic' is allocated/initialized in a special way, leaving | |
703 // the functions field NULL instead of empty. | |
704 const int func_len = class_functions.IsNull() ? 0 : class_functions.Length(); | |
705 for (int j = 0; j < func_len; j++) { | |
706 Function& function = Function::Handle(); | |
707 function ^= class_functions.At(j); | |
708 if (function.usage_counter() > 0) { | |
709 functions->Add(&function); | |
710 } | |
711 } | |
712 } | |
713 | |
714 | |
715 void Isolate::PrintInvokedFunctions() { | |
716 ASSERT(this == Isolate::Current()); | |
717 const GrowableObjectArray& libraries = | |
718 GrowableObjectArray::Handle(object_store()->libraries()); | |
719 Library& library = Library::Handle(); | |
720 GrowableArray<const Function*> invoked_functions; | |
721 for (int i = 0; i < libraries.Length(); i++) { | |
722 library ^= libraries.At(i); | |
723 Class& cls = Class::Handle(); | |
724 ClassDictionaryIterator iter(library, | |
725 ClassDictionaryIterator::kIteratePrivate); | |
726 while (iter.HasNext()) { | |
727 cls = iter.GetNextClass(); | |
728 AddFunctionsFromClass(cls, &invoked_functions); | |
729 } | |
730 } | |
731 invoked_functions.Sort(MostUsedFunctionFirst); | |
732 for (int i = 0; i < invoked_functions.length(); i++) { | |
733 OS::Print("%10" Pd " x %s\n", | |
734 invoked_functions[i]->usage_counter(), | |
735 invoked_functions[i]->ToFullyQualifiedCString()); | |
736 } | |
737 } | |
738 | |
739 | |
740 class FinalizeWeakPersistentHandlesVisitor : public HandleVisitor { | |
741 public: | |
742 FinalizeWeakPersistentHandlesVisitor() { | |
743 } | |
744 | |
745 void VisitHandle(uword addr) { | |
746 FinalizablePersistentHandle* handle = | |
747 reinterpret_cast<FinalizablePersistentHandle*>(addr); | |
748 FinalizablePersistentHandle::Finalize(handle); | |
749 } | |
750 | |
751 private: | |
752 DISALLOW_COPY_AND_ASSIGN(FinalizeWeakPersistentHandlesVisitor); | |
753 }; | |
754 | |
755 | |
756 void Isolate::Shutdown() { | |
757 ASSERT(this == Isolate::Current()); | |
758 ASSERT(top_resource() == NULL); | |
759 ASSERT((heap_ == NULL) || heap_->Verify()); | |
760 | |
761 // Create an area where we do have a zone and a handle scope so that we can | |
762 // call VM functions while tearing this isolate down. | |
763 { | |
764 StackZone stack_zone(this); | |
765 HandleScope handle_scope(this); | |
766 | |
767 if (FLAG_print_object_histogram) { | |
768 heap()->CollectAllGarbage(); | |
769 object_histogram()->Print(); | |
770 } | |
771 | |
772 // Clean up debugger resources. | |
773 debugger()->Shutdown(); | |
774 | |
775 // Close all the ports owned by this isolate. | |
776 PortMap::ClosePorts(message_handler()); | |
777 | |
778 // Fail fast if anybody tries to post any more messsages to this isolate. | |
779 delete message_handler(); | |
780 set_message_handler(NULL); | |
781 | |
782 // Dump all accumalated timer data for the isolate. | |
783 timer_list_.ReportTimers(); | |
784 if (FLAG_report_usage_count) { | |
785 PrintInvokedFunctions(); | |
786 } | |
787 | |
788 // Write out the coverage data if collection has been enabled. | |
789 CodeCoverage::Write(this); | |
790 | |
791 // Finalize any weak persistent handles with a non-null referent. | |
792 FinalizeWeakPersistentHandlesVisitor visitor; | |
793 api_state()->weak_persistent_handles().VisitHandles(&visitor); | |
794 | |
795 CompilerStats::Print(); | |
796 // TODO(asiva): Move this code to Dart::Cleanup when we have that method | |
797 // as the cleanup for Dart::InitOnce. | |
798 CodeObservers::DeleteAll(); | |
799 if (FLAG_trace_isolates) { | |
800 heap()->PrintSizes(); | |
801 megamorphic_cache_table()->PrintSizes(); | |
802 Symbols::DumpStats(); | |
803 OS::Print("[-] Stopping isolate:\n" | |
804 "\tisolate: %s\n", name()); | |
805 } | |
806 } | |
807 | |
808 // TODO(5411455): For now just make sure there are no current isolates | |
809 // as we are shutting down the isolate. | |
810 SetCurrent(NULL); | |
811 } | |
812 | |
813 | |
814 Dart_IsolateCreateCallback Isolate::create_callback_ = NULL; | |
815 Dart_IsolateInterruptCallback Isolate::interrupt_callback_ = NULL; | |
816 Dart_IsolateUnhandledExceptionCallback | |
817 Isolate::unhandled_exception_callback_ = NULL; | |
818 Dart_IsolateShutdownCallback Isolate::shutdown_callback_ = NULL; | |
819 Dart_FileOpenCallback Isolate::file_open_callback_ = NULL; | |
820 Dart_FileReadCallback Isolate::file_read_callback_ = NULL; | |
821 Dart_FileWriteCallback Isolate::file_write_callback_ = NULL; | |
822 Dart_FileCloseCallback Isolate::file_close_callback_ = NULL; | |
823 Dart_IsolateInterruptCallback Isolate::vmstats_callback_ = NULL; | |
824 | |
825 | |
826 void Isolate::VisitObjectPointers(ObjectPointerVisitor* visitor, | |
827 bool visit_prologue_weak_handles, | |
828 bool validate_frames) { | |
829 ASSERT(visitor != NULL); | |
830 | |
831 // Visit objects in the object store. | |
832 object_store()->VisitObjectPointers(visitor); | |
833 | |
834 // Visit objects in the class table. | |
835 class_table()->VisitObjectPointers(visitor); | |
836 | |
837 // Visit objects in the megamorphic cache. | |
838 megamorphic_cache_table()->VisitObjectPointers(visitor); | |
839 | |
840 // Visit objects in per isolate stubs. | |
841 StubCode::VisitObjectPointers(visitor); | |
842 | |
843 // Visit objects in zones. | |
844 current_zone()->VisitObjectPointers(visitor); | |
845 | |
846 // Visit objects in isolate specific handles area. | |
847 reusable_handles_.VisitObjectPointers(visitor); | |
848 | |
849 // Iterate over all the stack frames and visit objects on the stack. | |
850 StackFrameIterator frames_iterator(validate_frames); | |
851 StackFrame* frame = frames_iterator.NextFrame(); | |
852 while (frame != NULL) { | |
853 frame->VisitObjectPointers(visitor); | |
854 frame = frames_iterator.NextFrame(); | |
855 } | |
856 | |
857 // Visit the dart api state for all local and persistent handles. | |
858 if (api_state() != NULL) { | |
859 api_state()->VisitObjectPointers(visitor, visit_prologue_weak_handles); | |
860 } | |
861 | |
862 // Visit the top context which is stored in the isolate. | |
863 visitor->VisitPointer(reinterpret_cast<RawObject**>(&top_context_)); | |
864 | |
865 // Visit objects in the debugger. | |
866 debugger()->VisitObjectPointers(visitor); | |
867 } | |
868 | |
869 | |
870 void Isolate::VisitWeakPersistentHandles(HandleVisitor* visitor, | |
871 bool visit_prologue_weak_handles) { | |
872 if (api_state() != NULL) { | |
873 api_state()->VisitWeakHandles(visitor, visit_prologue_weak_handles); | |
874 } | |
875 } | |
876 | |
877 | |
878 static Monitor* status_sync = NULL; | |
879 | |
880 | |
881 bool Isolate::FetchStacktrace() { | |
882 Isolate* isolate = Isolate::Current(); | |
883 MonitorLocker ml(status_sync); | |
884 DebuggerStackTrace* stack = Debugger::CollectStackTrace(); | |
885 TextBuffer buffer(256); | |
886 buffer.Printf("{ \"handle\": \"0x%" Px64 "\", \"stacktrace\": [ ", | |
887 reinterpret_cast<int64_t>(isolate)); | |
888 intptr_t n_frames = stack->Length(); | |
889 String& url = String::Handle(); | |
890 String& function = String::Handle(); | |
891 for (int i = 0; i < n_frames; i++) { | |
892 if (i > 0) { | |
893 buffer.Printf(", "); | |
894 } | |
895 ActivationFrame* frame = stack->FrameAt(i); | |
896 url ^= frame->SourceUrl(); | |
897 function ^= frame->function().UserVisibleName(); | |
898 buffer.Printf("{ \"url\": \"%s\", ", url.ToCString()); | |
899 buffer.Printf("\"line\": %" Pd ", ", frame->LineNumber()); | |
900 buffer.Printf("\"function\": \"%s\", ", function.ToCString()); | |
901 | |
902 const Code& code = frame->code(); | |
903 buffer.Printf("\"code\": { "); | |
904 buffer.Printf("\"alive\": %s, ", code.is_alive() ? "false" : "true"); | |
905 buffer.Printf("\"optimized\": %s }}", | |
906 code.is_optimized() ? "false" : "true"); | |
907 } | |
908 buffer.Printf("]}"); | |
909 isolate->stacktrace_ = OS::StrNDup(buffer.buf(), buffer.length()); | |
910 ml.Notify(); | |
911 return true; | |
912 } | |
913 | |
914 | |
915 bool Isolate::FetchStackFrameDetails() { | |
916 Isolate* isolate = Isolate::Current(); | |
917 ASSERT(isolate->stack_frame_index_ >= 0); | |
918 MonitorLocker ml(status_sync); | |
919 DebuggerStackTrace* stack = Debugger::CollectStackTrace(); | |
920 intptr_t frame_index = isolate->stack_frame_index_; | |
921 if (frame_index >= stack->Length()) { | |
922 // Frame no longer available. | |
923 return false; | |
924 } | |
925 ActivationFrame* frame = stack->FrameAt(frame_index); | |
926 TextBuffer buffer(256); | |
927 buffer.Printf("{ \"handle\": \"0x%" Px64 "\", \"frame_index\": %" Pd ", ", | |
928 reinterpret_cast<int64_t>(isolate), frame_index); | |
929 | |
930 const Code& code = frame->code(); | |
931 buffer.Printf("\"code\": { \"size\": %" Pd ", ", code.Size()); | |
932 buffer.Printf("\"alive\": %s, ", code.is_alive() ? "false" : "true"); | |
933 buffer.Printf("\"optimized\": %s }, ", | |
934 code.is_optimized() ? "false" : "true"); | |
935 // TODO(tball): add compilation stats (time, etc.), when available. | |
936 | |
937 buffer.Printf("\"local_vars\": [ "); | |
938 intptr_t n_local_vars = frame->NumLocalVariables(); | |
939 String& var_name = String::Handle(); | |
940 Instance& value = Instance::Handle(); | |
941 for (int i = 0; i < n_local_vars; i++) { | |
942 if (i > 0) { | |
943 buffer.Printf(", "); | |
944 } | |
945 intptr_t token_pos, end_pos; | |
946 frame->VariableAt(i, &var_name, &token_pos, &end_pos, &value); | |
947 buffer.Printf( | |
948 "{ \"name\": \"%s\", \"pos\": %" Pd ", \"end_pos\": %" Pd ", " | |
949 "\"value\": \"%s\" }", | |
950 var_name.ToCString(), token_pos, end_pos, value.ToCString()); | |
951 } | |
952 buffer.Printf("]}"); | |
953 isolate->stacktrace_ = OS::StrNDup(buffer.buf(), buffer.length()); | |
954 ml.Notify(); | |
955 return true; | |
956 } | |
957 | |
958 | |
959 char* Isolate::DoStacktraceInterrupt(Dart_IsolateInterruptCallback cb) { | |
960 ASSERT(stacktrace_ == NULL); | |
961 SetVmStatsCallback(cb); | |
962 if (status_sync == NULL) { | |
963 status_sync = new Monitor(); | |
964 } | |
965 if (is_runnable()) { | |
966 ScheduleInterrupts(Isolate::kVmStatusInterrupt); | |
967 { | |
968 MonitorLocker ml(status_sync); | |
969 if (stacktrace_ == NULL) { // It may already be available. | |
970 ml.Wait(1000); | |
971 } | |
972 } | |
973 SetVmStatsCallback(NULL); | |
974 } | |
975 char* result = stacktrace_; | |
976 stacktrace_ = NULL; | |
977 if (result == NULL) { | |
978 // Return empty stack. | |
979 TextBuffer buffer(256); | |
980 buffer.Printf("{ \"handle\": \"0x%" Px64 "\", \"stacktrace\": []}", | |
981 reinterpret_cast<int64_t>(this)); | |
982 | |
983 result = OS::StrNDup(buffer.buf(), buffer.length()); | |
984 } | |
985 ASSERT(result != NULL); | |
986 // result is freed by VmStats::WebServer(). | |
987 return result; | |
988 } | |
989 | |
990 | |
991 char* Isolate::GetStatusStacktrace() { | |
992 return DoStacktraceInterrupt(&FetchStacktrace); | |
993 } | |
994 | |
995 char* Isolate::GetStatusStackFrame(intptr_t index) { | |
996 ASSERT(index >= 0); | |
997 stack_frame_index_ = index; | |
998 char* result = DoStacktraceInterrupt(&FetchStackFrameDetails); | |
999 stack_frame_index_ = -1; | |
1000 return result; | |
1001 } | |
1002 | |
1003 | |
1004 // Returns the isolate's general detail information. | |
1005 char* Isolate::GetStatusDetails() { | |
1006 const char* format = "{\n" | |
1007 " \"handle\": \"0x%" Px64 "\",\n" | |
1008 " \"name\": \"%s\",\n" | |
1009 " \"port\": %" Pd ",\n" | |
1010 " \"starttime\": %" Pd ",\n" | |
1011 " \"stacklimit\": %" Pd ",\n" | |
1012 " \"newspace\": {\n" | |
1013 " \"used\": %" Pd ",\n" | |
1014 " \"capacity\": %" Pd "\n" | |
1015 " },\n" | |
1016 " \"oldspace\": {\n" | |
1017 " \"used\": %" Pd ",\n" | |
1018 " \"capacity\": %" Pd "\n" | |
1019 " }\n" | |
1020 "}"; | |
1021 char buffer[300]; | |
1022 int64_t address = reinterpret_cast<int64_t>(this); | |
1023 int n = OS::SNPrint(buffer, 300, format, address, name(), main_port(), | |
1024 (start_time() / 1000L), saved_stack_limit(), | |
1025 heap()->Used(Heap::kNew) / KB, | |
1026 heap()->Capacity(Heap::kNew) / KB, | |
1027 heap()->Used(Heap::kOld) / KB, | |
1028 heap()->Capacity(Heap::kOld) / KB); | |
1029 ASSERT(n < 300); | |
1030 return strdup(buffer); | |
1031 } | |
1032 | |
1033 | |
1034 char* Isolate::GetStatus(const char* request) { | |
1035 char* p = const_cast<char*>(request); | |
1036 const char* service_type = "/isolate/"; | |
1037 ASSERT(!strncmp(p, service_type, strlen(service_type))); | |
1038 p += strlen(service_type); | |
1039 | |
1040 // Extract isolate handle. | |
1041 int64_t addr; | |
1042 OS::StringToInt64(p, &addr); | |
1043 // TODO(tball): add validity check when issue 9600 is fixed. | |
1044 Isolate* isolate = reinterpret_cast<Isolate*>(addr); | |
1045 p += strcspn(p, "/"); | |
1046 | |
1047 // Query "/isolate/<handle>". | |
1048 if (strlen(p) == 0) { | |
1049 return isolate->GetStatusDetails(); | |
1050 } | |
1051 | |
1052 // Query "/isolate/<handle>/stacktrace" | |
1053 if (!strcmp(p, "/stacktrace")) { | |
1054 return isolate->GetStatusStacktrace(); | |
1055 } | |
1056 | |
1057 // Query "/isolate/<handle>/stacktrace/<frame-index>" | |
1058 const char* stacktrace_query = "/stacktrace/"; | |
1059 int64_t frame_index = -1; | |
1060 if (!strncmp(p, stacktrace_query, strlen(stacktrace_query))) { | |
1061 p += strlen(stacktrace_query); | |
1062 OS::StringToInt64(p, &frame_index); | |
1063 if (frame_index >= 0) { | |
1064 return isolate->GetStatusStackFrame(frame_index); | |
1065 } | |
1066 } | |
1067 | |
1068 // TODO(tball): "/isolate/<handle>/stacktrace/<frame-index>"/disassemble" | |
1069 | |
1070 return NULL; // Unimplemented query. | |
1071 } | |
1072 | |
1073 | |
1074 template<class T> | |
1075 T* Isolate::AllocateReusableHandle() { | |
1076 T* handle = reinterpret_cast<T*>(reusable_handles_.AllocateScopedHandle()); | |
1077 T::initializeHandle(handle, T::null()); | |
1078 return handle; | |
1079 } | |
1080 | |
1081 | |
1082 static void FillDeferredSlots(DeferredSlot** slot_list) { | |
1083 DeferredSlot* slot = *slot_list; | |
1084 *slot_list = NULL; | |
1085 | |
1086 while (slot != NULL) { | |
1087 DeferredSlot* current = slot; | |
1088 slot = slot->next(); | |
1089 | |
1090 current->Materialize(); | |
1091 | |
1092 delete current; | |
1093 } | |
1094 } | |
1095 | |
1096 | |
1097 void Isolate::MaterializeDeferredBoxes() { | |
1098 FillDeferredSlots(&deferred_boxes_); | |
1099 } | |
1100 | |
1101 | |
1102 void Isolate::MaterializeDeferredObjects() { | |
1103 FillDeferredSlots(&deferred_object_refs_); | |
1104 } | |
1105 | |
1106 | |
1107 static char* GetRootScriptUri(Isolate* isolate) { | |
1108 const Library& library = | |
1109 Library::Handle(isolate->object_store()->root_library()); | |
1110 ASSERT(!library.IsNull()); | |
1111 const String& script_name = String::Handle(library.url()); | |
1112 return isolate->current_zone()->MakeCopyOfString(script_name.ToCString()); | |
1113 } | |
1114 | |
1115 | |
1116 IsolateSpawnState::IsolateSpawnState(const Function& func, | |
1117 const Function& callback_func) | |
1118 : isolate_(NULL), | |
1119 script_url_(NULL), | |
1120 library_url_(NULL), | |
1121 function_name_(NULL), | |
1122 exception_callback_name_(NULL) { | |
1123 script_url_ = strdup(GetRootScriptUri(Isolate::Current())); | |
1124 const Class& cls = Class::Handle(func.Owner()); | |
1125 ASSERT(cls.IsTopLevel()); | |
1126 const Library& lib = Library::Handle(cls.library()); | |
1127 const String& lib_url = String::Handle(lib.url()); | |
1128 library_url_ = strdup(lib_url.ToCString()); | |
1129 | |
1130 const String& func_name = String::Handle(func.name()); | |
1131 function_name_ = strdup(func_name.ToCString()); | |
1132 if (!callback_func.IsNull()) { | |
1133 const String& callback_name = String::Handle(callback_func.name()); | |
1134 exception_callback_name_ = strdup(callback_name.ToCString()); | |
1135 } else { | |
1136 exception_callback_name_ = strdup("_unhandledExceptionCallback"); | |
1137 } | |
1138 } | |
1139 | |
1140 | |
1141 IsolateSpawnState::IsolateSpawnState(const char* script_url) | |
1142 : isolate_(NULL), | |
1143 library_url_(NULL), | |
1144 function_name_(NULL), | |
1145 exception_callback_name_(NULL) { | |
1146 script_url_ = strdup(script_url); | |
1147 library_url_ = NULL; | |
1148 function_name_ = strdup("main"); | |
1149 exception_callback_name_ = strdup("_unhandledExceptionCallback"); | |
1150 } | |
1151 | |
1152 | |
1153 IsolateSpawnState::~IsolateSpawnState() { | |
1154 free(script_url_); | |
1155 free(library_url_); | |
1156 free(function_name_); | |
1157 free(exception_callback_name_); | |
1158 } | |
1159 | |
1160 | |
1161 RawObject* IsolateSpawnState::ResolveFunction() { | |
1162 // Resolve the library. | |
1163 Library& lib = Library::Handle(); | |
1164 if (library_url()) { | |
1165 const String& lib_url = String::Handle(String::New(library_url())); | |
1166 lib = Library::LookupLibrary(lib_url); | |
1167 if (lib.IsNull() || lib.IsError()) { | |
1168 const String& msg = String::Handle(String::NewFormatted( | |
1169 "Unable to find library '%s'.", library_url())); | |
1170 return LanguageError::New(msg); | |
1171 } | |
1172 } else { | |
1173 lib = isolate()->object_store()->root_library(); | |
1174 } | |
1175 ASSERT(!lib.IsNull()); | |
1176 | |
1177 // Resolve the function. | |
1178 const String& func_name = | |
1179 String::Handle(String::New(function_name())); | |
1180 const Function& func = Function::Handle(lib.LookupLocalFunction(func_name)); | |
1181 if (func.IsNull()) { | |
1182 const String& msg = String::Handle(String::NewFormatted( | |
1183 "Unable to resolve function '%s' in library '%s'.", | |
1184 function_name(), (library_url() ? library_url() : script_url()))); | |
1185 return LanguageError::New(msg); | |
1186 } | |
1187 return func.raw(); | |
1188 } | |
1189 | |
1190 | |
1191 void IsolateSpawnState::Cleanup() { | |
1192 SwitchIsolateScope switch_scope(isolate()); | |
1193 Dart::ShutdownIsolate(); | |
1194 } | |
1195 | |
1196 } // namespace dart | 127 } // namespace dart |
OLD | NEW |