| 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 | |
| 32 | namespace dart { |
| 33 | |
| 34 | DEFINE_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 | |
| 45 | DEFINE_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 | |
| 51 | DEFINE_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 | |
| 60 | DEFINE_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 | |
| 68 | DEFINE_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 | |
| 73 | DEFINE_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 | |
| 80 | DEFINE_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 | |
| 89 | DEFINE_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 | |
| 94 | DEFINE_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 | |
| 103 | static 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 | |
| 111 | DEFINE_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 | |
| 130 | class 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 | |
| 140 | static 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. |
| 296 | DEFINE_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 | |
| 347 | class 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 | |
| 426 | static 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 | |
| 433 | IsolateSpawnState::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 | |
| 467 | IsolateSpawnState::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 | |
| 497 | IsolateSpawnState::~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 | |
| 506 | ObjectPtr 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 | |
| 587 | static 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 | |
| 598 | ObjectPtr 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 | |
| 605 | ObjectPtr 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 | |
| 612 | static 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 | |
| 618 | class 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 | |
| 901 | static 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 | |
| 910 | DEFINE_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 | |
| 968 | static 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 | |
| 999 | DEFINE_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 | |
| 1065 | DEFINE_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 | |
| 1074 | DEFINE_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 | |
| 1084 | DEFINE_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 | |
| 1090 | DEFINE_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 | |
| 1124 | DEFINE_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 | |
| 1137 | DEFINE_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 | |
| 1162 | static void ExternalTypedDataFinalizer(void* isolate_callback_data, |
| 1163 | void* peer) { |
| 1164 | free(ptr: peer); |
| 1165 | } |
| 1166 | |
| 1167 | static 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 | |
| 1179 | DEFINE_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 | |
| 1243 | DEFINE_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 | |