1// Copyright (c) 2012, 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#include <utility>
7
8#include "include/dart_native_api.h"
9#include "platform/assert.h"
10#include "platform/unicode.h"
11#include "vm/bootstrap_natives.h"
12#include "vm/class_finalizer.h"
13#include "vm/dart.h"
14#include "vm/dart_api_impl.h"
15#include "vm/dart_api_message.h"
16#include "vm/dart_entry.h"
17#include "vm/exceptions.h"
18#include "vm/hash_table.h"
19#include "vm/lockers.h"
20#include "vm/longjump.h"
21#include "vm/message_handler.h"
22#include "vm/message_snapshot.h"
23#include "vm/object.h"
24#include "vm/object_graph_copy.h"
25#include "vm/object_store.h"
26#include "vm/port.h"
27#include "vm/resolver.h"
28#include "vm/service.h"
29#include "vm/snapshot.h"
30#include "vm/symbols.h"
31
32namespace dart {
33
34DEFINE_NATIVE_ENTRY(Capability_factory, 0, 1) {
35 ASSERT(
36 TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull());
37 // Keep capability IDs less than 2^53 so web clients of the service
38 // protocol can process it properly.
39 //
40 // See https://github.com/dart-lang/sdk/issues/53081.
41 uint64_t id = isolate->random()->NextJSInt();
42 return Capability::New(id);
43}
44
45DEFINE_NATIVE_ENTRY(Capability_equals, 0, 2) {
46 GET_NON_NULL_NATIVE_ARGUMENT(Capability, recv, arguments->NativeArgAt(0));
47 GET_NON_NULL_NATIVE_ARGUMENT(Capability, other, arguments->NativeArgAt(1));
48 return (recv.Id() == other.Id()) ? Bool::True().ptr() : Bool::False().ptr();
49}
50
51DEFINE_NATIVE_ENTRY(Capability_get_hashcode, 0, 1) {
52 GET_NON_NULL_NATIVE_ARGUMENT(Capability, cap, arguments->NativeArgAt(0));
53 int64_t id = cap.Id();
54 int32_t hi = static_cast<int32_t>(id >> 32);
55 int32_t lo = static_cast<int32_t>(id);
56 int32_t hash = (hi ^ lo) & kSmiMax;
57 return Smi::New(value: hash);
58}
59
60DEFINE_NATIVE_ENTRY(RawReceivePort_factory, 0, 2) {
61 ASSERT(
62 TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull());
63 GET_NON_NULL_NATIVE_ARGUMENT(String, debug_name, arguments->NativeArgAt(1));
64 Dart_Port port_id = PortMap::CreatePort(handler: isolate->message_handler());
65 return ReceivePort::New(id: port_id, debug_name, is_control_port: false /* not control port */);
66}
67
68DEFINE_NATIVE_ENTRY(RawReceivePort_get_id, 0, 1) {
69 GET_NON_NULL_NATIVE_ARGUMENT(ReceivePort, port, arguments->NativeArgAt(0));
70 return Integer::New(value: port.Id());
71}
72
73DEFINE_NATIVE_ENTRY(RawReceivePort_closeInternal, 0, 1) {
74 GET_NON_NULL_NATIVE_ARGUMENT(ReceivePort, port, arguments->NativeArgAt(0));
75 Dart_Port id = port.Id();
76 PortMap::ClosePort(id);
77 return Integer::New(value: id);
78}
79
80DEFINE_NATIVE_ENTRY(RawReceivePort_setActive, 0, 2) {
81 GET_NON_NULL_NATIVE_ARGUMENT(ReceivePort, port, arguments->NativeArgAt(0));
82 GET_NON_NULL_NATIVE_ARGUMENT(Bool, active, arguments->NativeArgAt(1));
83 Dart_Port id = port.Id();
84 PortMap::SetPortState(
85 id, kind: active.value() ? PortMap::kLivePort : PortMap::kInactivePort);
86 return Object::null();
87}
88
89DEFINE_NATIVE_ENTRY(SendPort_get_id, 0, 1) {
90 GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
91 return Integer::New(value: port.Id());
92}
93
94DEFINE_NATIVE_ENTRY(SendPort_get_hashcode, 0, 1) {
95 GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
96 int64_t id = port.Id();
97 int32_t hi = static_cast<int32_t>(id >> 32);
98 int32_t lo = static_cast<int32_t>(id);
99 int32_t hash = (hi ^ lo) & kSmiMax;
100 return Smi::New(value: hash);
101}
102
103static bool InSameGroup(Isolate* sender, const SendPort& receiver) {
104 // Cannot determine whether sender is in same group (yet).
105 if (sender->origin_id() == ILLEGAL_PORT) return false;
106
107 // Only allow arbitrary messages between isolates of the same IG.
108 return sender->origin_id() == receiver.origin_id();
109}
110
111DEFINE_NATIVE_ENTRY(SendPort_sendInternal_, 0, 2) {
112 GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
113 GET_NON_NULL_NATIVE_ARGUMENT(Instance, obj, arguments->NativeArgAt(1));
114
115 const Dart_Port destination_port_id = port.Id();
116 const bool same_group = InSameGroup(sender: isolate, receiver: port);
117#if defined(DEBUG)
118 if (same_group) {
119 ASSERT(PortMap::IsReceiverInThisIsolateGroupOrClosed(destination_port_id,
120 isolate->group()));
121 }
122#endif
123
124 // TODO(turnidge): Throw an exception when the return value is false?
125 PortMap::PostMessage(message: WriteMessage(same_group, obj, dest_port: destination_port_id,
126 priority: Message::kNormalPriority));
127 return Object::null();
128}
129
130class UntaggedObjectPtrSetTraits {
131 public:
132 static bool ReportStats() { return false; }
133 static const char* Name() { return "UntaggedObjectPtrSetTraits"; }
134
135 static bool IsMatch(const ObjectPtr a, const ObjectPtr b) { return a == b; }
136
137 static uword Hash(const ObjectPtr obj) { return static_cast<uword>(obj); }
138};
139
140static ObjectPtr ValidateMessageObject(Zone* zone,
141 Isolate* isolate,
142 const Object& obj) {
143 TIMELINE_DURATION(Thread::Current(), Isolate, "ValidateMessageObject");
144
145 class SendMessageValidator : public ObjectPointerVisitor {
146 public:
147 SendMessageValidator(IsolateGroup* isolate_group,
148 WeakTable* visited,
149 MallocGrowableArray<ObjectPtr>* const working_set)
150 : ObjectPointerVisitor(isolate_group),
151 visited_(visited),
152 working_set_(working_set) {}
153
154 void VisitObject(ObjectPtr obj) {
155 if (!obj->IsHeapObject() || obj->untag()->IsCanonical()) {
156 return;
157 }
158 if (visited_->GetValueExclusive(key: obj) == 1) {
159 return;
160 }
161 visited_->SetValueExclusive(key: obj, val: 1);
162 working_set_->Add(value: obj);
163 }
164
165 private:
166 void VisitPointers(ObjectPtr* from, ObjectPtr* to) override {
167 for (ObjectPtr* ptr = from; ptr <= to; ptr++) {
168 VisitObject(obj: *ptr);
169 }
170 }
171
172#if defined(DART_COMPRESSED_POINTERS)
173 void VisitCompressedPointers(uword heap_base,
174 CompressedObjectPtr* from,
175 CompressedObjectPtr* to) override {
176 for (CompressedObjectPtr* ptr = from; ptr <= to; ptr++) {
177 VisitObject(ptr->Decompress(heap_base));
178 }
179 }
180#endif
181
182 WeakTable* visited_;
183 MallocGrowableArray<ObjectPtr>* const working_set_;
184 };
185 if (!obj.ptr()->IsHeapObject() || obj.ptr()->untag()->IsCanonical()) {
186 return obj.ptr();
187 }
188 ClassTable* class_table = isolate->group()->class_table();
189
190 Class& klass = Class::Handle(zone);
191 Closure& closure = Closure::Handle(zone);
192 Array& array = Array::Handle(zone);
193 Object& illegal_object = Object::Handle(zone);
194 const char* exception_message = nullptr;
195 Thread* thread = Thread::Current();
196
197 // working_set contains only elements that have not been visited yet that
198 // need to be processed.
199 // So before adding elements to working_set ensure to check visited flag,
200 // set visited flag at the same time as the element is added.
201
202 // This working set of raw pointers is visited by GC, only one for a given
203 // isolate should be in use.
204 MallocGrowableArray<ObjectPtr>* const working_set =
205 isolate->pointers_to_verify_at_exit();
206 ASSERT(working_set->length() == 0);
207 std::unique_ptr<WeakTable> visited(new WeakTable());
208
209 SendMessageValidator visitor(isolate->group(), visited.get(), working_set);
210
211 visited->SetValueExclusive(key: obj.ptr(), val: 1);
212 working_set->Add(value: obj.ptr());
213
214 while (!working_set->is_empty() && (exception_message == nullptr)) {
215 thread->CheckForSafepoint();
216
217 ObjectPtr raw = working_set->RemoveLast();
218 if (CanShareObjectAcrossIsolates(obj: raw)) {
219 continue;
220 }
221 const intptr_t cid = raw->GetClassId();
222 switch (cid) {
223 case kArrayCid: {
224 array ^= Array::RawCast(raw);
225 visitor.VisitObject(obj: array.GetTypeArguments());
226 const intptr_t batch_size = (2 << 14) - 1;
227 for (intptr_t i = 0; i < array.Length(); ++i) {
228 ObjectPtr ptr = array.At(index: i);
229 visitor.VisitObject(obj: ptr);
230 if ((i & batch_size) == batch_size) {
231 thread->CheckForSafepoint();
232 }
233 }
234 continue;
235 }
236 case kClosureCid:
237 closure ^= raw;
238 // Only context has to be checked.
239 working_set->Add(value: closure.context());
240 continue;
241
242#define MESSAGE_SNAPSHOT_ILLEGAL(type) \
243 case k##type##Cid: \
244 illegal_object = raw; \
245 exception_message = "is a " #type; \
246 break;
247
248 MESSAGE_SNAPSHOT_ILLEGAL(DynamicLibrary);
249 // TODO(http://dartbug.com/47777): Send and exit support: remove this.
250 MESSAGE_SNAPSHOT_ILLEGAL(Finalizer);
251 MESSAGE_SNAPSHOT_ILLEGAL(NativeFinalizer);
252 MESSAGE_SNAPSHOT_ILLEGAL(MirrorReference);
253 MESSAGE_SNAPSHOT_ILLEGAL(Pointer);
254 MESSAGE_SNAPSHOT_ILLEGAL(ReceivePort);
255 MESSAGE_SNAPSHOT_ILLEGAL(UserTag);
256 MESSAGE_SNAPSHOT_ILLEGAL(SuspendState);
257
258 default:
259 klass = class_table->At(cid);
260 if (klass.is_isolate_unsendable()) {
261 illegal_object = raw;
262 exception_message =
263 "is unsendable object (see restrictions listed at"
264 "`SendPort.send()` documentation for more information)";
265 break;
266 }
267 }
268 raw->untag()->VisitPointers(visitor: &visitor);
269 }
270
271 ASSERT((exception_message == nullptr) == illegal_object.IsNull());
272 if (exception_message != nullptr) {
273 working_set->Clear();
274
275 const Array& args = Array::Handle(zone, ptr: Array::New(len: 3));
276 args.SetAt(index: 0, value: illegal_object);
277 args.SetAt(index: 2, value: String::Handle(
278 zone, ptr: String::NewFormatted(
279 format: "%s%s",
280 FindRetainingPath(
281 zone, isolate, from: obj, to: illegal_object,
282 traversal_rules: TraversalRules::kInternalToIsolateGroup),
283 exception_message)));
284 const Object& exception = Object::Handle(
285 zone, ptr: Exceptions::Create(type: Exceptions::kArgumentValue, arguments: args));
286 return UnhandledException::New(exception: Instance::Cast(obj: exception),
287 stacktrace: StackTrace::Handle(zone));
288 }
289
290 ASSERT(working_set->length() == 0);
291 isolate->set_forward_table_new(nullptr);
292 return obj.ptr();
293}
294
295// TODO(http://dartbug.com/47777): Add support for Finalizers.
296DEFINE_NATIVE_ENTRY(Isolate_exit_, 0, 2) {
297 GET_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
298 if (!port.IsNull()) {
299 GET_NATIVE_ARGUMENT(Instance, obj, arguments->NativeArgAt(1));
300
301 const bool same_group = InSameGroup(sender: isolate, receiver: port);
302#if defined(DEBUG)
303 if (same_group) {
304 ASSERT(PortMap::IsReceiverInThisIsolateGroupOrClosed(port.Id(),
305 isolate->group()));
306 }
307#endif
308 if (!same_group) {
309 const auto& error =
310 String::Handle(ptr: String::New(cstr: "exit with final message is only allowed "
311 "for isolates in one isolate group."));
312 Exceptions::ThrowArgumentError(arg: error);
313 UNREACHABLE();
314 }
315
316 Object& validated_result = Object::Handle(zone);
317 const Object& msg_obj = Object::Handle(zone, ptr: obj.ptr());
318 validated_result = ValidateMessageObject(zone, isolate, obj: msg_obj);
319 // msg_array = [
320 // <message>,
321 // <collection-lib-objects-to-rehash>,
322 // <core-lib-objects-to-rehash>,
323 // ]
324 const Array& msg_array = Array::Handle(zone, ptr: Array::New(len: 3));
325 msg_array.SetAt(index: 0, value: msg_obj);
326 if (validated_result.IsUnhandledException()) {
327 Exceptions::PropagateError(error: Error::Cast(obj: validated_result));
328 UNREACHABLE();
329 }
330 PersistentHandle* handle =
331 isolate->group()->api_state()->AllocatePersistentHandle();
332 handle->set_ptr(msg_array);
333 isolate->bequeath(bequest: std::unique_ptr<Bequest>(new Bequest(handle, port.Id())));
334 }
335
336 Thread::Current()->StartUnwindError();
337 const String& msg =
338 String::Handle(ptr: String::New(cstr: "isolate terminated by Isolate.exit"));
339 const UnwindError& error = UnwindError::Handle(ptr: UnwindError::New(message: msg));
340 error.set_is_user_initiated(true);
341 Exceptions::PropagateError(error);
342 UNREACHABLE();
343 // We will never execute dart code again in this isolate.
344 return Object::null();
345}
346
347class IsolateSpawnState {
348 public:
349 IsolateSpawnState(Dart_Port parent_port,
350 Dart_Port origin_id,
351 const char* script_url,
352 PersistentHandle* closure_tuple_handle,
353 SerializedObjectBuffer* message_buffer,
354 const char* package_config,
355 bool paused,
356 bool errorsAreFatal,
357 Dart_Port onExit,
358 Dart_Port onError,
359 const char* debug_name,
360 IsolateGroup* group);
361 IsolateSpawnState(Dart_Port parent_port,
362 const char* script_url,
363 const char* package_config,
364 SerializedObjectBuffer* args_buffer,
365 SerializedObjectBuffer* message_buffer,
366 bool paused,
367 bool errorsAreFatal,
368 Dart_Port onExit,
369 Dart_Port onError,
370 const char* debug_name,
371 IsolateGroup* group);
372 ~IsolateSpawnState();
373
374 Isolate* isolate() const { return isolate_; }
375 void set_isolate(Isolate* value) { isolate_ = value; }
376
377 Dart_Port parent_port() const { return parent_port_; }
378 Dart_Port origin_id() const { return origin_id_; }
379 Dart_Port on_exit_port() const { return on_exit_port_; }
380 Dart_Port on_error_port() const { return on_error_port_; }
381 const char* script_url() const { return script_url_; }
382 const char* package_config() const { return package_config_; }
383 const char* library_url() const { return library_url_; }
384 const char* class_name() const { return class_name_; }
385 const char* function_name() const { return function_name_; }
386 const char* debug_name() const { return debug_name_; }
387 bool is_spawn_uri() const {
388 return library_url_ == nullptr && // No top-level entrypoint.
389 closure_tuple_handle_ == nullptr; // No closure entrypoint.
390 }
391 bool paused() const { return paused_; }
392 bool errors_are_fatal() const { return errors_are_fatal_; }
393 Dart_IsolateFlags* isolate_flags() { return &isolate_flags_; }
394 PersistentHandle* closure_tuple_handle() const {
395 return closure_tuple_handle_;
396 }
397
398 ObjectPtr ResolveFunction();
399 ObjectPtr BuildArgs(Thread* thread);
400 ObjectPtr BuildMessage(Thread* thread);
401
402 IsolateGroup* isolate_group() const { return isolate_group_; }
403
404 private:
405 Isolate* isolate_ = nullptr;
406 Dart_Port parent_port_;
407 Dart_Port origin_id_ = ILLEGAL_PORT;
408 Dart_Port on_exit_port_;
409 Dart_Port on_error_port_;
410 const char* script_url_;
411 const char* package_config_;
412 const char* library_url_ = nullptr;
413 const char* class_name_ = nullptr;
414 const char* function_name_ = nullptr;
415 const char* debug_name_;
416 PersistentHandle* closure_tuple_handle_ = nullptr;
417 IsolateGroup* isolate_group_;
418 std::unique_ptr<Message> serialized_args_;
419 std::unique_ptr<Message> serialized_message_;
420
421 Dart_IsolateFlags isolate_flags_;
422 bool paused_;
423 bool errors_are_fatal_;
424};
425
426static const char* NewConstChar(const char* chars) {
427 size_t len = strlen(s: chars);
428 char* mem = new char[len + 1];
429 memmove(dest: mem, src: chars, n: len + 1);
430 return mem;
431}
432
433IsolateSpawnState::IsolateSpawnState(Dart_Port parent_port,
434 Dart_Port origin_id,
435 const char* script_url,
436 PersistentHandle* closure_tuple_handle,
437 SerializedObjectBuffer* message_buffer,
438 const char* package_config,
439 bool paused,
440 bool errors_are_fatal,
441 Dart_Port on_exit_port,
442 Dart_Port on_error_port,
443 const char* debug_name,
444 IsolateGroup* isolate_group)
445 : parent_port_(parent_port),
446 origin_id_(origin_id),
447 on_exit_port_(on_exit_port),
448 on_error_port_(on_error_port),
449 script_url_(script_url),
450 package_config_(package_config),
451 debug_name_(debug_name),
452 closure_tuple_handle_(closure_tuple_handle),
453 isolate_group_(isolate_group),
454 serialized_args_(nullptr),
455 serialized_message_(message_buffer->StealMessage()),
456 paused_(paused),
457 errors_are_fatal_(errors_are_fatal) {
458 ASSERT(closure_tuple_handle_ != nullptr);
459
460 auto thread = Thread::Current();
461 auto isolate = thread->isolate();
462
463 // Inherit flags from spawning isolate.
464 isolate->FlagsCopyTo(api_flags: isolate_flags());
465}
466
467IsolateSpawnState::IsolateSpawnState(Dart_Port parent_port,
468 const char* script_url,
469 const char* package_config,
470 SerializedObjectBuffer* args_buffer,
471 SerializedObjectBuffer* message_buffer,
472 bool paused,
473 bool errors_are_fatal,
474 Dart_Port on_exit_port,
475 Dart_Port on_error_port,
476 const char* debug_name,
477 IsolateGroup* isolate_group)
478 : parent_port_(parent_port),
479 on_exit_port_(on_exit_port),
480 on_error_port_(on_error_port),
481 script_url_(script_url),
482 package_config_(package_config),
483 debug_name_(debug_name),
484 isolate_group_(isolate_group),
485 serialized_args_(args_buffer->StealMessage()),
486 serialized_message_(message_buffer->StealMessage()),
487 isolate_flags_(),
488 paused_(paused),
489 errors_are_fatal_(errors_are_fatal) {
490 function_name_ = NewConstChar(chars: "main");
491
492 // By default inherit flags from spawning isolate. These can be overridden
493 // from the calling code.
494 Isolate::Current()->FlagsCopyTo(api_flags: isolate_flags());
495}
496
497IsolateSpawnState::~IsolateSpawnState() {
498 delete[] script_url_;
499 delete[] package_config_;
500 delete[] library_url_;
501 delete[] class_name_;
502 delete[] function_name_;
503 delete[] debug_name_;
504}
505
506ObjectPtr IsolateSpawnState::ResolveFunction() {
507 Thread* thread = Thread::Current();
508 auto IG = thread->isolate_group();
509 Zone* zone = thread->zone();
510
511 const String& func_name = String::Handle(zone, ptr: String::New(cstr: function_name()));
512
513 if (library_url() == nullptr) {
514 // Handle spawnUri lookup rules.
515 // Check whether the root library defines a main function.
516 const Library& lib =
517 Library::Handle(zone, ptr: IG->object_store()->root_library());
518 Function& func = Function::Handle(zone, ptr: lib.LookupLocalFunction(name: func_name));
519 if (func.IsNull()) {
520 // Check whether main is reexported from the root library.
521 const Object& obj = Object::Handle(zone, ptr: lib.LookupReExport(name: func_name));
522 if (obj.IsFunction()) {
523 func ^= obj.ptr();
524 }
525 }
526 if (func.IsNull()) {
527 const String& msg = String::Handle(
528 zone, ptr: String::NewFormatted(
529 format: "Unable to resolve function '%s' in script '%s'.",
530 function_name(), script_url()));
531 return LanguageError::New(formatted_message: msg);
532 }
533 return func.ptr();
534 }
535
536 // Lookup the to be spawned function for the Isolate.spawn implementation.
537 // Resolve the library.
538 const String& lib_url = String::Handle(zone, ptr: String::New(cstr: library_url()));
539 const Library& lib =
540 Library::Handle(zone, ptr: Library::LookupLibrary(thread, url: lib_url));
541 if (lib.IsNull() || lib.IsError()) {
542 const String& msg = String::Handle(
543 zone,
544 ptr: String::NewFormatted(format: "Unable to find library '%s'.", library_url()));
545 return LanguageError::New(formatted_message: msg);
546 }
547
548 // Resolve the function.
549 if (class_name() == nullptr) {
550 const Function& func =
551 Function::Handle(zone, ptr: lib.LookupLocalFunction(name: func_name));
552 if (func.IsNull()) {
553 const String& msg = String::Handle(
554 zone, ptr: String::NewFormatted(
555 format: "Unable to resolve function '%s' in library '%s'.",
556 function_name(), library_url()));
557 return LanguageError::New(formatted_message: msg);
558 }
559 return func.ptr();
560 }
561
562 const String& cls_name = String::Handle(zone, ptr: String::New(cstr: class_name()));
563 const Class& cls = Class::Handle(zone, ptr: lib.LookupLocalClass(name: cls_name));
564 if (cls.IsNull()) {
565 const String& msg = String::Handle(
566 zone, ptr: String::NewFormatted(
567 format: "Unable to resolve class '%s' in library '%s'.", class_name(),
568 (library_url() != nullptr ? library_url() : script_url())));
569 return LanguageError::New(formatted_message: msg);
570 }
571 Function& func = Function::Handle(zone);
572 const auto& error = cls.EnsureIsFinalized(thread);
573 if (error == Error::null()) {
574 func = cls.LookupStaticFunctionAllowPrivate(name: func_name);
575 }
576 if (func.IsNull()) {
577 const String& msg = String::Handle(
578 zone, ptr: String::NewFormatted(
579 format: "Unable to resolve static method '%s.%s' in library '%s'.",
580 class_name(), function_name(),
581 (library_url() != nullptr ? library_url() : script_url())));
582 return LanguageError::New(formatted_message: msg);
583 }
584 return func.ptr();
585}
586
587static ObjectPtr DeserializeMessage(Thread* thread, Message* message) {
588 if (message == nullptr) {
589 return Object::null();
590 }
591 if (message->IsRaw()) {
592 return Object::RawCast(obj: message->raw_obj());
593 } else {
594 return ReadMessage(thread, message);
595 }
596}
597
598ObjectPtr IsolateSpawnState::BuildArgs(Thread* thread) {
599 const Object& result =
600 Object::Handle(ptr: DeserializeMessage(thread, message: serialized_args_.get()));
601 serialized_args_.reset();
602 return result.ptr();
603}
604
605ObjectPtr IsolateSpawnState::BuildMessage(Thread* thread) {
606 const Object& result =
607 Object::Handle(ptr: DeserializeMessage(thread, message: serialized_message_.get()));
608 serialized_message_.reset();
609 return result.ptr();
610}
611
612static void ThrowIsolateSpawnException(const String& message) {
613 const Array& args = Array::Handle(ptr: Array::New(len: 1));
614 args.SetAt(index: 0, value: message);
615 Exceptions::ThrowByType(type: Exceptions::kIsolateSpawn, arguments: args);
616}
617
618class SpawnIsolateTask : public ThreadPool::Task {
619 public:
620 SpawnIsolateTask(Isolate* parent_isolate,
621 std::unique_ptr<IsolateSpawnState> state)
622 : parent_isolate_(parent_isolate), state_(std::move(state)) {
623 parent_isolate->IncrementSpawnCount();
624 }
625
626 ~SpawnIsolateTask() override {
627 if (parent_isolate_ != nullptr) {
628 parent_isolate_->DecrementSpawnCount();
629 }
630 }
631
632 void Run() override {
633 const char* name = (state_->debug_name() == nullptr)
634 ? state_->function_name()
635 : state_->debug_name();
636 ASSERT(name != nullptr);
637
638 auto group = state_->isolate_group();
639 if (group == nullptr) {
640 RunHeavyweight(name);
641 } else {
642 RunLightweight(name);
643 }
644 }
645
646 void RunHeavyweight(const char* name) {
647 // The create isolate group callback is mandatory. If not provided we
648 // cannot spawn isolates.
649 auto create_group_callback = Isolate::CreateGroupCallback();
650 if (create_group_callback == nullptr) {
651 FailedSpawn(error: "Isolate spawn is not supported by this Dart embedder\n");
652 return;
653 }
654
655 char* error = nullptr;
656
657 // Make a copy of the state's isolate flags and hand it to the callback.
658 Dart_IsolateFlags api_flags = *(state_->isolate_flags());
659 api_flags.is_system_isolate = false;
660 Dart_Isolate isolate =
661 (create_group_callback)(state_->script_url(), name, nullptr,
662 state_->package_config(), &api_flags,
663 parent_isolate_->init_callback_data(), &error);
664 parent_isolate_->DecrementSpawnCount();
665 parent_isolate_ = nullptr;
666
667 if (isolate == nullptr) {
668 FailedSpawn(error, /*has_current_isolate=*/false);
669 free(ptr: error);
670 return;
671 }
672 Dart_EnterIsolate(isolate);
673 Run(child: reinterpret_cast<Isolate*>(isolate));
674 }
675
676 void RunLightweight(const char* name) {
677 // The create isolate initialize callback is mandatory.
678 auto initialize_callback = Isolate::InitializeCallback();
679 if (initialize_callback == nullptr) {
680 FailedSpawn(
681 error: "Lightweight isolate spawn is not supported by this Dart embedder\n",
682 /*has_current_isolate=*/false);
683 return;
684 }
685
686 char* error = nullptr;
687
688 auto group = state_->isolate_group();
689 Isolate* isolate = CreateWithinExistingIsolateGroup(group, name, error: &error);
690 parent_isolate_->DecrementSpawnCount();
691 parent_isolate_ = nullptr;
692
693 if (isolate == nullptr) {
694 FailedSpawn(error, /*has_current_isolate=*/false);
695 free(ptr: error);
696 return;
697 }
698
699 void* child_isolate_data = nullptr;
700 const bool success = initialize_callback(&child_isolate_data, &error);
701 if (!success) {
702 FailedSpawn(error);
703 Dart_ShutdownIsolate();
704 free(ptr: error);
705 return;
706 }
707
708 isolate->set_init_callback_data(child_isolate_data);
709 Run(child: isolate);
710 }
711
712 private:
713 void Run(Isolate* child) {
714 if (!EnsureIsRunnable(child)) {
715 Dart_ShutdownIsolate();
716 return;
717 }
718
719 state_->set_isolate(child);
720 if (state_->origin_id() != ILLEGAL_PORT) {
721 // origin_id is set to parent isolate main port id when spawning via
722 // spawnFunction.
723 child->set_origin_id(state_->origin_id());
724 }
725
726 bool success = true;
727 {
728 auto thread = Thread::Current();
729 TransitionNativeToVM transition(thread);
730 StackZone zone(thread);
731 HandleScope hs(thread);
732
733 success = EnqueueEntrypointInvocationAndNotifySpawner(thread);
734 }
735
736 if (!success) {
737 state_ = nullptr;
738 Dart_ShutdownIsolate();
739 return;
740 }
741
742 // All preconditions are met for this to always succeed.
743 char* error = nullptr;
744 if (!Dart_RunLoopAsync(errors_are_fatal: state_->errors_are_fatal(), on_error_port: state_->on_error_port(),
745 on_exit_port: state_->on_exit_port(), error: &error)) {
746 FATAL("Dart_RunLoopAsync() failed: %s. Please file a Dart VM bug report.",
747 error);
748 }
749 }
750
751 bool EnsureIsRunnable(Isolate* child) {
752 // We called out to the embedder to create/initialize a new isolate. The
753 // embedder callback successfully did so. It is now our responsibility to
754 // run the isolate.
755 // If the isolate was not marked as runnable, we'll do so here and run it.
756 if (!child->is_runnable()) {
757 const char* error = child->MakeRunnable();
758 if (error != nullptr) {
759 FailedSpawn(error);
760 return false;
761 }
762 }
763 ASSERT(child->is_runnable());
764 return true;
765 }
766
767 bool EnqueueEntrypointInvocationAndNotifySpawner(Thread* thread) {
768 auto isolate = thread->isolate();
769 auto zone = thread->zone();
770 const bool is_spawn_uri = state_->is_spawn_uri();
771
772 // Step 1) Resolve the entrypoint function.
773 auto& entrypoint_closure = Closure::Handle(zone);
774 if (state_->closure_tuple_handle() != nullptr) {
775 const auto& result = Object::Handle(
776 zone,
777 ptr: ReadObjectGraphCopyMessage(thread, handle: state_->closure_tuple_handle()));
778 if (result.IsError()) {
779 ReportError(
780 error: "Failed to deserialize the passed entrypoint to the new isolate.");
781 return false;
782 }
783 entrypoint_closure = Closure::RawCast(raw: result.ptr());
784 } else {
785 const auto& result = Object::Handle(zone, ptr: state_->ResolveFunction());
786 if (result.IsError()) {
787 ASSERT(is_spawn_uri);
788 ReportError(error: "Failed to resolve entrypoint function.");
789 return false;
790 }
791 ASSERT(result.IsFunction());
792 auto& func = Function::Handle(zone, ptr: Function::Cast(obj: result).ptr());
793 func = func.ImplicitClosureFunction();
794 entrypoint_closure = func.ImplicitStaticClosure();
795 }
796
797 // Step 2) Enqueue delayed invocation of entrypoint callback.
798 const auto& args_obj = Object::Handle(zone, ptr: state_->BuildArgs(thread));
799 if (args_obj.IsError()) {
800 ReportError(
801 error: "Failed to deserialize the passed arguments to the new isolate.");
802 return false;
803 }
804 ASSERT(args_obj.IsNull() || args_obj.IsInstance());
805 const auto& message_obj =
806 Object::Handle(zone, ptr: state_->BuildMessage(thread));
807 if (message_obj.IsError()) {
808 ReportError(
809 error: "Failed to deserialize the passed arguments to the new isolate.");
810 return false;
811 }
812 ASSERT(message_obj.IsNull() || message_obj.IsInstance());
813 const Array& args = Array::Handle(zone, ptr: Array::New(len: 4));
814 args.SetAt(index: 0, value: entrypoint_closure);
815 args.SetAt(index: 1, value: args_obj);
816 args.SetAt(index: 2, value: message_obj);
817 args.SetAt(index: 3, value: is_spawn_uri ? Bool::True() : Bool::False());
818
819 const auto& lib = Library::Handle(zone, ptr: Library::IsolateLibrary());
820 const auto& entry_name = String::Handle(zone, ptr: String::New(cstr: "_startIsolate"));
821 const auto& entry_point =
822 Function::Handle(zone, ptr: lib.LookupLocalFunction(name: entry_name));
823 ASSERT(entry_point.IsFunction() && !entry_point.IsNull());
824 const auto& result =
825 Object::Handle(zone, ptr: DartEntry::InvokeFunction(function: entry_point, arguments: args));
826 if (result.IsError()) {
827 ReportError(error: "Failed to enqueue delayed entrypoint invocation.");
828 return false;
829 }
830
831 // Step 3) Pause the isolate if required & Notify parent isolate about
832 // isolate creation.
833 const auto& capabilities = Array::Handle(zone, ptr: Array::New(len: 2));
834 auto& capability = Capability::Handle(zone);
835 capability = Capability::New(id: isolate->pause_capability());
836 capabilities.SetAt(index: 0, value: capability);
837 capability = Capability::New(id: isolate->terminate_capability());
838 capabilities.SetAt(index: 1, value: capability);
839 const auto& send_port =
840 SendPort::Handle(zone, ptr: SendPort::New(id: isolate->main_port()));
841 const auto& message = Array::Handle(zone, ptr: Array::New(len: 2));
842 message.SetAt(index: 0, value: send_port);
843 message.SetAt(index: 1, value: capabilities);
844 if (state_->paused()) {
845 capability ^= capabilities.At(index: 0);
846 const bool added = isolate->AddResumeCapability(capability);
847 ASSERT(added);
848 isolate->message_handler()->increment_paused();
849 }
850 {
851 // If parent isolate died, we ignore the fact that we cannot notify it.
852 PortMap::PostMessage(message: WriteMessage(/*same_group=*/false, obj: message,
853 dest_port: state_->parent_port(),
854 priority: Message::kNormalPriority));
855 }
856
857 return true;
858 }
859
860 void FailedSpawn(const char* error, bool has_current_isolate = true) {
861 ReportError(error: error != nullptr
862 ? error
863 : "Unknown error occurred during Isolate spawning.");
864 // Destruction of [IsolateSpawnState] may cause destruction of [Message]
865 // which make need to delete persistent handles (which requires a current
866 // isolate group).
867 if (has_current_isolate) {
868 ASSERT(IsolateGroup::Current() == state_->isolate_group());
869 state_ = nullptr;
870 } else if (state_->isolate_group() != nullptr) {
871 ASSERT(IsolateGroup::Current() == nullptr);
872 const bool kBypassSafepoint = false;
873 const bool result = Thread::EnterIsolateGroupAsHelper(
874 isolate_group: state_->isolate_group(), kind: Thread::kUnknownTask, bypass_safepoint: kBypassSafepoint);
875 ASSERT(result);
876 state_ = nullptr;
877 Thread::ExitIsolateGroupAsHelper(bypass_safepoint: kBypassSafepoint);
878 } else {
879 // The state won't need a current isolate group, because it belongs to a
880 // [Isolate.spawnUri] call.
881 state_ = nullptr;
882 }
883 }
884
885 void ReportError(const char* error) {
886 Dart_CObject error_cobj;
887 error_cobj.type = Dart_CObject_kString;
888 error_cobj.value.as_string = const_cast<char*>(error);
889 if (!Dart_PostCObject(port_id: state_->parent_port(), message: &error_cobj)) {
890 // Perhaps the parent isolate died or closed the port before we
891 // could report the error. Ignore.
892 }
893 }
894
895 Isolate* parent_isolate_;
896 std::unique_ptr<IsolateSpawnState> state_;
897
898 DISALLOW_COPY_AND_ASSIGN(SpawnIsolateTask);
899};
900
901static const char* String2UTF8(const String& str) {
902 intptr_t len = Utf8::Length(str);
903 char* result = new char[len + 1];
904 str.ToUTF8(utf8_array: reinterpret_cast<uint8_t*>(result), array_len: len);
905 result[len] = 0;
906
907 return result;
908}
909
910DEFINE_NATIVE_ENTRY(Isolate_spawnFunction, 0, 10) {
911 GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
912 GET_NON_NULL_NATIVE_ARGUMENT(String, script_uri, arguments->NativeArgAt(1));
913 GET_NON_NULL_NATIVE_ARGUMENT(Closure, closure, arguments->NativeArgAt(2));
914 GET_NON_NULL_NATIVE_ARGUMENT(Instance, message, arguments->NativeArgAt(3));
915 GET_NON_NULL_NATIVE_ARGUMENT(Bool, paused, arguments->NativeArgAt(4));
916 GET_NATIVE_ARGUMENT(Bool, fatalErrors, arguments->NativeArgAt(5));
917 GET_NATIVE_ARGUMENT(SendPort, onExit, arguments->NativeArgAt(6));
918 GET_NATIVE_ARGUMENT(SendPort, onError, arguments->NativeArgAt(7));
919 GET_NATIVE_ARGUMENT(String, packageConfig, arguments->NativeArgAt(8));
920 GET_NATIVE_ARGUMENT(String, debugName, arguments->NativeArgAt(9));
921
922 PersistentHandle* closure_tuple_handle = nullptr;
923 // We have a non-toplevel closure that we might need to copy.
924 // Result will be [<closure-copy>, <objects-in-msg-to-rehash>]
925 const auto& closure_copy_tuple = Object::Handle(
926 zone, ptr: CopyMutableObjectGraph(root: closure)); // Throws if it fails.
927 ASSERT(closure_copy_tuple.IsArray());
928 ASSERT(
929 Object::Handle(zone, Array::Cast(closure_copy_tuple).At(0)).IsClosure());
930 closure_tuple_handle =
931 isolate->group()->api_state()->AllocatePersistentHandle();
932 closure_tuple_handle->set_ptr(closure_copy_tuple.ptr());
933
934 bool fatal_errors = fatalErrors.IsNull() ? true : fatalErrors.value();
935 Dart_Port on_exit_port = onExit.IsNull() ? ILLEGAL_PORT : onExit.Id();
936 Dart_Port on_error_port = onError.IsNull() ? ILLEGAL_PORT : onError.Id();
937
938 // We first try to serialize the message. In case the message is not
939 // serializable this will throw an exception.
940 SerializedObjectBuffer message_buffer;
941 message_buffer.set_message(WriteMessage(
942 /*same_group=*/true, obj: message, ILLEGAL_PORT, priority: Message::kNormalPriority));
943
944 const char* utf8_package_config =
945 packageConfig.IsNull() ? nullptr : String2UTF8(str: packageConfig);
946 const char* utf8_debug_name =
947 debugName.IsNull() ? nullptr : String2UTF8(str: debugName);
948 if (closure_tuple_handle != nullptr && utf8_debug_name == nullptr) {
949 const auto& closure_function = Function::Handle(zone, ptr: closure.function());
950 utf8_debug_name =
951 NewConstChar(chars: closure_function.QualifiedUserVisibleNameCString());
952 }
953
954 std::unique_ptr<IsolateSpawnState> state(new IsolateSpawnState(
955 port.Id(), isolate->origin_id(), String2UTF8(str: script_uri),
956 closure_tuple_handle, &message_buffer, utf8_package_config,
957 paused.value(), fatal_errors, on_exit_port, on_error_port,
958 utf8_debug_name, isolate->group()));
959
960 // Since this is a call to Isolate.spawn, copy the parent isolate's code.
961 state->isolate_flags()->copy_parent_code = true;
962
963 isolate->group()->thread_pool()->Run<SpawnIsolateTask>(args&: isolate,
964 args: std::move(state));
965 return Object::null();
966}
967
968static const char* CanonicalizeUri(Thread* thread,
969 const Library& library,
970 const String& uri,
971 char** error) {
972 const char* result = nullptr;
973 Zone* zone = thread->zone();
974 auto isolate_group = thread->isolate_group();
975 if (isolate_group->HasTagHandler()) {
976 const Object& obj = Object::Handle(
977 ptr: isolate_group->CallTagHandler(tag: Dart_kCanonicalizeUrl, arg1: library, arg2: uri));
978 if (obj.IsString()) {
979 result = String2UTF8(str: String::Cast(obj));
980 } else if (obj.IsError()) {
981 Error& error_obj = Error::Handle();
982 error_obj ^= obj.ptr();
983 *error = zone->PrintToString(format: "Unable to canonicalize uri '%s': %s",
984 uri.ToCString(), error_obj.ToErrorCString());
985 } else {
986 *error = zone->PrintToString(
987 format: "Unable to canonicalize uri '%s': "
988 "library tag handler returned wrong type",
989 uri.ToCString());
990 }
991 } else {
992 *error = zone->PrintToString(
993 format: "Unable to canonicalize uri '%s': no library tag handler found.",
994 uri.ToCString());
995 }
996 return result;
997}
998
999DEFINE_NATIVE_ENTRY(Isolate_spawnUri, 0, 12) {
1000 GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
1001 GET_NON_NULL_NATIVE_ARGUMENT(String, uri, arguments->NativeArgAt(1));
1002 GET_NON_NULL_NATIVE_ARGUMENT(Instance, args, arguments->NativeArgAt(2));
1003 GET_NON_NULL_NATIVE_ARGUMENT(Instance, message, arguments->NativeArgAt(3));
1004 GET_NON_NULL_NATIVE_ARGUMENT(Bool, paused, arguments->NativeArgAt(4));
1005 GET_NATIVE_ARGUMENT(SendPort, onExit, arguments->NativeArgAt(5));
1006 GET_NATIVE_ARGUMENT(SendPort, onError, arguments->NativeArgAt(6));
1007 GET_NATIVE_ARGUMENT(Bool, fatalErrors, arguments->NativeArgAt(7));
1008 GET_NATIVE_ARGUMENT(Bool, checked, arguments->NativeArgAt(8));
1009 GET_NATIVE_ARGUMENT(Array, environment, arguments->NativeArgAt(9));
1010 GET_NATIVE_ARGUMENT(String, packageConfig, arguments->NativeArgAt(10));
1011 GET_NATIVE_ARGUMENT(String, debugName, arguments->NativeArgAt(11));
1012
1013 bool fatal_errors = fatalErrors.IsNull() ? true : fatalErrors.value();
1014 Dart_Port on_exit_port = onExit.IsNull() ? ILLEGAL_PORT : onExit.Id();
1015 Dart_Port on_error_port = onError.IsNull() ? ILLEGAL_PORT : onError.Id();
1016
1017 // We first try to serialize the arguments and the message. In case the
1018 // arguments or the message are not serializable this will throw an exception.
1019 SerializedObjectBuffer arguments_buffer;
1020 SerializedObjectBuffer message_buffer;
1021 {
1022 arguments_buffer.set_message(WriteMessage(
1023 /*same_group=*/false, obj: args, ILLEGAL_PORT, priority: Message::kNormalPriority));
1024 }
1025 {
1026 message_buffer.set_message(WriteMessage(
1027 /*same_group=*/false, obj: message, ILLEGAL_PORT, priority: Message::kNormalPriority));
1028 }
1029
1030 // Canonicalize the uri with respect to the current isolate.
1031 const Library& root_lib =
1032 Library::Handle(ptr: isolate->group()->object_store()->root_library());
1033 char* error = nullptr;
1034 const char* canonical_uri = CanonicalizeUri(thread, library: root_lib, uri, error: &error);
1035 if (canonical_uri == nullptr) {
1036 const String& msg = String::Handle(ptr: String::New(cstr: error));
1037 ThrowIsolateSpawnException(message: msg);
1038 }
1039
1040 const char* utf8_package_config =
1041 packageConfig.IsNull() ? nullptr : String2UTF8(str: packageConfig);
1042 const char* utf8_debug_name =
1043 debugName.IsNull() ? nullptr : String2UTF8(str: debugName);
1044
1045 std::unique_ptr<IsolateSpawnState> state(new IsolateSpawnState(
1046 port.Id(), canonical_uri, utf8_package_config, &arguments_buffer,
1047 &message_buffer, paused.value(), fatal_errors, on_exit_port,
1048 on_error_port, utf8_debug_name, /*group=*/nullptr));
1049
1050 // If we were passed a value then override the default flags state for
1051 // checked mode.
1052 if (!checked.IsNull()) {
1053 Dart_IsolateFlags* flags = state->isolate_flags();
1054 flags->enable_asserts = checked.value();
1055 }
1056
1057 // Since this is a call to Isolate.spawnUri, don't copy the parent's code.
1058 state->isolate_flags()->copy_parent_code = false;
1059
1060 isolate->group()->thread_pool()->Run<SpawnIsolateTask>(args&: isolate,
1061 args: std::move(state));
1062 return Object::null();
1063}
1064
1065DEFINE_NATIVE_ENTRY(Isolate_getDebugName, 0, 1) {
1066 GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
1067 auto name = Isolate::LookupIsolateNameByPort(port: port.Id());
1068 if (name == nullptr) {
1069 return String::null();
1070 }
1071 return String::New(cstr: name.get());
1072}
1073
1074DEFINE_NATIVE_ENTRY(Isolate_getPortAndCapabilitiesOfCurrentIsolate, 0, 0) {
1075 const Array& result = Array::Handle(ptr: Array::New(len: 3));
1076 result.SetAt(index: 0, value: SendPort::Handle(ptr: SendPort::New(id: isolate->main_port())));
1077 result.SetAt(
1078 index: 1, value: Capability::Handle(ptr: Capability::New(id: isolate->pause_capability())));
1079 result.SetAt(
1080 index: 2, value: Capability::Handle(ptr: Capability::New(id: isolate->terminate_capability())));
1081 return result.ptr();
1082}
1083
1084DEFINE_NATIVE_ENTRY(Isolate_getCurrentRootUriStr, 0, 0) {
1085 const Library& root_lib =
1086 Library::Handle(zone, ptr: isolate->group()->object_store()->root_library());
1087 return root_lib.url();
1088}
1089
1090DEFINE_NATIVE_ENTRY(Isolate_registerKernelBlob, 0, 1) {
1091 GET_NON_NULL_NATIVE_ARGUMENT(TypedData, kernel_blob,
1092 arguments->NativeArgAt(0));
1093 auto register_kernel_blob_callback = Isolate::RegisterKernelBlobCallback();
1094 if (register_kernel_blob_callback == nullptr) {
1095 Exceptions::ThrowUnsupportedError(
1096 msg: "Registration of kernel blobs is not supported by this Dart embedder.");
1097 }
1098 bool is_kernel = false;
1099 {
1100 NoSafepointScope no_safepoint;
1101 is_kernel =
1102 Dart_IsKernel(buffer: reinterpret_cast<uint8_t*>(kernel_blob.DataAddr(byte_offset: 0)),
1103 buffer_size: kernel_blob.LengthInBytes());
1104 }
1105 if (!is_kernel) {
1106 const auto& error = String::Handle(
1107 zone, ptr: String::New(cstr: "kernelBlob doesn\'t contain a valid kernel.\n"));
1108 Exceptions::ThrowArgumentError(arg: error);
1109 UNREACHABLE();
1110 }
1111 const char* uri = nullptr;
1112 {
1113 NoSafepointScope no_safepoint;
1114 uri = register_kernel_blob_callback(
1115 reinterpret_cast<uint8_t*>(kernel_blob.DataAddr(byte_offset: 0)),
1116 kernel_blob.LengthInBytes());
1117 }
1118 if (uri == nullptr) {
1119 Exceptions::ThrowOOM();
1120 }
1121 return String::New(cstr: uri);
1122}
1123
1124DEFINE_NATIVE_ENTRY(Isolate_unregisterKernelBlob, 0, 1) {
1125 GET_NON_NULL_NATIVE_ARGUMENT(String, kernel_blob_uri,
1126 arguments->NativeArgAt(0));
1127 auto unregister_kernel_blob_callback =
1128 Isolate::UnregisterKernelBlobCallback();
1129 if (unregister_kernel_blob_callback == nullptr) {
1130 Exceptions::ThrowUnsupportedError(
1131 msg: "Registration of kernel blobs is not supported by this Dart embedder.");
1132 }
1133 unregister_kernel_blob_callback(kernel_blob_uri.ToCString());
1134 return Object::null();
1135}
1136
1137DEFINE_NATIVE_ENTRY(Isolate_sendOOB, 0, 2) {
1138 GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0));
1139 GET_NON_NULL_NATIVE_ARGUMENT(Array, msg, arguments->NativeArgAt(1));
1140
1141 // Make sure to route this request to the isolate library OOB message handler.
1142 msg.SetAt(index: 0, value: Smi::Handle(ptr: Smi::New(value: Message::kIsolateLibOOBMsg)));
1143
1144 // Ensure message writer (and it's resources, e.g. forwarding tables) are
1145 // cleaned up before handling interrupts.
1146 {
1147 PortMap::PostMessage(message: WriteMessage(/*same_group=*/false, obj: msg, dest_port: port.Id(),
1148 priority: Message::kOOBPriority));
1149 }
1150
1151 // Drain interrupts before running so any IMMEDIATE operations on the current
1152 // isolate happen synchronously.
1153 const Error& error = Error::Handle(ptr: thread->HandleInterrupts());
1154 if (!error.IsNull()) {
1155 Exceptions::PropagateError(error);
1156 UNREACHABLE();
1157 }
1158
1159 return Object::null();
1160}
1161
1162static void ExternalTypedDataFinalizer(void* isolate_callback_data,
1163 void* peer) {
1164 free(ptr: peer);
1165}
1166
1167static intptr_t GetTypedDataSizeOrThrow(const Instance& instance) {
1168 // From the Dart side we are guaranteed that the type of [instance] is a
1169 // subtype of TypedData.
1170 if (instance.IsTypedDataBase()) {
1171 return TypedDataBase::Cast(obj: instance).LengthInBytes();
1172 }
1173
1174 // This can happen if [instance] is `null` or an instance of a 3rd party class
1175 // which implements [TypedData].
1176 Exceptions::ThrowArgumentError(arg: instance);
1177}
1178
1179DEFINE_NATIVE_ENTRY(TransferableTypedData_factory, 0, 2) {
1180 ASSERT(
1181 TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull());
1182
1183 GET_NON_NULL_NATIVE_ARGUMENT(Instance, array_instance,
1184 arguments->NativeArgAt(1));
1185
1186 Array& array = Array::Handle();
1187 intptr_t array_length;
1188 if (array_instance.IsGrowableObjectArray()) {
1189 const auto& growable_array = GrowableObjectArray::Cast(obj: array_instance);
1190 array ^= growable_array.data();
1191 array_length = growable_array.Length();
1192 } else if (array_instance.IsArray()) {
1193 array ^= Array::Cast(obj: array_instance).ptr();
1194 array_length = array.Length();
1195 } else {
1196 Exceptions::ThrowArgumentError(arg: array_instance);
1197 UNREACHABLE();
1198 }
1199 Instance& instance = Instance::Handle();
1200 uint64_t total_bytes = 0;
1201 const uint64_t kMaxBytes = TypedData::MaxElements(class_id: kTypedDataUint8ArrayCid);
1202 for (intptr_t i = 0; i < array_length; i++) {
1203 instance ^= array.At(index: i);
1204 total_bytes += static_cast<uintptr_t>(GetTypedDataSizeOrThrow(instance));
1205 if (total_bytes > kMaxBytes) {
1206 const Array& error_args = Array::Handle(ptr: Array::New(len: 3));
1207 error_args.SetAt(index: 0, value: array);
1208 error_args.SetAt(index: 1, value: String::Handle(ptr: String::New(cstr: "data")));
1209 error_args.SetAt(
1210 index: 2, value: String::Handle(ptr: String::NewFormatted(
1211 format: "Aggregated list exceeds max size %" Pu64 "", kMaxBytes)));
1212 Exceptions::ThrowByType(type: Exceptions::kArgumentValue, arguments: error_args);
1213 UNREACHABLE();
1214 }
1215 }
1216
1217 uint8_t* data = reinterpret_cast<uint8_t*>(::malloc(size: total_bytes));
1218 if (data == nullptr) {
1219 const Instance& exception = Instance::Handle(
1220 ptr: thread->isolate_group()->object_store()->out_of_memory());
1221 Exceptions::Throw(thread, exception);
1222 UNREACHABLE();
1223 }
1224 intptr_t offset = 0;
1225 for (intptr_t i = 0; i < array_length; i++) {
1226 instance ^= array.At(index: i);
1227
1228 {
1229 NoSafepointScope no_safepoint;
1230 const auto& typed_data = TypedDataBase::Cast(obj: instance);
1231 const intptr_t length_in_bytes = typed_data.LengthInBytes();
1232
1233 void* source = typed_data.DataAddr(byte_offset: 0);
1234 // The memory does not overlap.
1235 memcpy(dest: data + offset, src: source, n: length_in_bytes); // NOLINT
1236 offset += length_in_bytes;
1237 }
1238 }
1239 ASSERT(static_cast<uintptr_t>(offset) == total_bytes);
1240 return TransferableTypedData::New(data, len: total_bytes);
1241}
1242
1243DEFINE_NATIVE_ENTRY(TransferableTypedData_materialize, 0, 1) {
1244 GET_NON_NULL_NATIVE_ARGUMENT(TransferableTypedData, t,
1245 arguments->NativeArgAt(0));
1246
1247 void* peer;
1248 {
1249 NoSafepointScope no_safepoint;
1250 peer = thread->heap()->GetPeer(raw_obj: t.ptr());
1251 // Assume that object's Peer is only used to track transferability state.
1252 ASSERT(peer != nullptr);
1253 }
1254
1255 TransferableTypedDataPeer* tpeer =
1256 reinterpret_cast<TransferableTypedDataPeer*>(peer);
1257 const intptr_t length = tpeer->length();
1258 uint8_t* data = tpeer->data();
1259 if (data == nullptr) {
1260 const auto& error = String::Handle(ptr: String::New(
1261 cstr: "Attempt to materialize object that was transferred already."));
1262 Exceptions::ThrowArgumentError(arg: error);
1263 UNREACHABLE();
1264 }
1265 tpeer->handle()->EnsureFreedExternal(isolate_group: IsolateGroup::Current());
1266 tpeer->ClearData();
1267
1268 const ExternalTypedData& typed_data = ExternalTypedData::Handle(
1269 ptr: ExternalTypedData::New(class_id: kExternalTypedDataUint8ArrayCid, data, len: length,
1270 space: thread->heap()->SpaceForExternal(size: length)));
1271 FinalizablePersistentHandle* finalizable_ref =
1272 FinalizablePersistentHandle::New(isolate_group: thread->isolate_group(), object: typed_data,
1273 /* peer= */ data,
1274 callback: &ExternalTypedDataFinalizer, external_size: length,
1275 /*auto_delete=*/true);
1276 ASSERT(finalizable_ref != nullptr);
1277 return typed_data.ptr();
1278}
1279
1280} // namespace dart
1281

source code of flutter_engine/third_party/dart/runtime/lib/isolate.cc