| 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/object.h" |
| 23 | #include "vm/object_store.h" |
| 24 | #include "vm/port.h" |
| 25 | #include "vm/resolver.h" |
| 26 | #include "vm/service.h" |
| 27 | #include "vm/snapshot.h" |
| 28 | #include "vm/symbols.h" |
| 29 | |
| 30 | namespace dart { |
| 31 | |
| 32 | DEFINE_NATIVE_ENTRY(CapabilityImpl_factory, 0, 1) { |
| 33 | ASSERT( |
| 34 | TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull()); |
| 35 | uint64_t id = isolate->random()->NextUInt64(); |
| 36 | return Capability::New(id); |
| 37 | } |
| 38 | |
| 39 | DEFINE_NATIVE_ENTRY(CapabilityImpl_equals, 0, 2) { |
| 40 | GET_NON_NULL_NATIVE_ARGUMENT(Capability, recv, arguments->NativeArgAt(0)); |
| 41 | GET_NON_NULL_NATIVE_ARGUMENT(Capability, other, arguments->NativeArgAt(1)); |
| 42 | return (recv.Id() == other.Id()) ? Bool::True().raw() : Bool::False().raw(); |
| 43 | } |
| 44 | |
| 45 | DEFINE_NATIVE_ENTRY(CapabilityImpl_get_hashcode, 0, 1) { |
| 46 | GET_NON_NULL_NATIVE_ARGUMENT(Capability, cap, arguments->NativeArgAt(0)); |
| 47 | int64_t id = cap.Id(); |
| 48 | int32_t hi = static_cast<int32_t>(id >> 32); |
| 49 | int32_t lo = static_cast<int32_t>(id); |
| 50 | int32_t hash = (hi ^ lo) & kSmiMax; |
| 51 | return Smi::New(hash); |
| 52 | } |
| 53 | |
| 54 | DEFINE_NATIVE_ENTRY(RawReceivePortImpl_factory, 0, 1) { |
| 55 | ASSERT( |
| 56 | TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull()); |
| 57 | Dart_Port port_id = PortMap::CreatePort(isolate->message_handler()); |
| 58 | return ReceivePort::New(port_id, false /* not control port */); |
| 59 | } |
| 60 | |
| 61 | DEFINE_NATIVE_ENTRY(RawReceivePortImpl_get_id, 0, 1) { |
| 62 | GET_NON_NULL_NATIVE_ARGUMENT(ReceivePort, port, arguments->NativeArgAt(0)); |
| 63 | return Integer::New(port.Id()); |
| 64 | } |
| 65 | |
| 66 | DEFINE_NATIVE_ENTRY(RawReceivePortImpl_get_sendport, 0, 1) { |
| 67 | GET_NON_NULL_NATIVE_ARGUMENT(ReceivePort, port, arguments->NativeArgAt(0)); |
| 68 | return port.send_port(); |
| 69 | } |
| 70 | |
| 71 | DEFINE_NATIVE_ENTRY(RawReceivePortImpl_closeInternal, 0, 1) { |
| 72 | GET_NON_NULL_NATIVE_ARGUMENT(ReceivePort, port, arguments->NativeArgAt(0)); |
| 73 | Dart_Port id = port.Id(); |
| 74 | PortMap::ClosePort(id); |
| 75 | return Integer::New(id); |
| 76 | } |
| 77 | |
| 78 | DEFINE_NATIVE_ENTRY(SendPortImpl_get_id, 0, 1) { |
| 79 | GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0)); |
| 80 | return Integer::New(port.Id()); |
| 81 | } |
| 82 | |
| 83 | DEFINE_NATIVE_ENTRY(SendPortImpl_get_hashcode, 0, 1) { |
| 84 | GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0)); |
| 85 | int64_t id = port.Id(); |
| 86 | int32_t hi = static_cast<int32_t>(id >> 32); |
| 87 | int32_t lo = static_cast<int32_t>(id); |
| 88 | int32_t hash = (hi ^ lo) & kSmiMax; |
| 89 | return Smi::New(hash); |
| 90 | } |
| 91 | |
| 92 | DEFINE_NATIVE_ENTRY(SendPortImpl_sendInternal_, 0, 2) { |
| 93 | GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0)); |
| 94 | // TODO(iposva): Allow for arbitrary messages to be sent. |
| 95 | GET_NON_NULL_NATIVE_ARGUMENT(Instance, obj, arguments->NativeArgAt(1)); |
| 96 | |
| 97 | const Dart_Port destination_port_id = port.Id(); |
| 98 | const bool can_send_any_object = isolate->origin_id() == port.origin_id(); |
| 99 | |
| 100 | if (ApiObjectConverter::CanConvert(obj.raw())) { |
| 101 | PortMap::PostMessage( |
| 102 | Message::New(destination_port_id, obj.raw(), Message::kNormalPriority)); |
| 103 | } else { |
| 104 | MessageWriter writer(can_send_any_object); |
| 105 | // TODO(turnidge): Throw an exception when the return value is false? |
| 106 | PortMap::PostMessage(writer.WriteMessage(obj, destination_port_id, |
| 107 | Message::kNormalPriority)); |
| 108 | } |
| 109 | return Object::null(); |
| 110 | } |
| 111 | |
| 112 | class ObjectPtrSetTraitsLayout { |
| 113 | public: |
| 114 | static bool ReportStats() { return false; } |
| 115 | static const char* Name() { return "RawObjectPtrSetTraits" ; } |
| 116 | |
| 117 | static bool IsMatch(const ObjectPtr a, const ObjectPtr b) { return a == b; } |
| 118 | |
| 119 | static uword Hash(const ObjectPtr obj) { return static_cast<uword>(obj); } |
| 120 | }; |
| 121 | |
| 122 | static ObjectPtr ValidateMessageObject(Zone* zone, |
| 123 | Isolate* isolate, |
| 124 | const Object& obj) { |
| 125 | TIMELINE_DURATION(Thread::Current(), Isolate, "ValidateMessageObject" ); |
| 126 | |
| 127 | class SendMessageValidator : public ObjectPointerVisitor { |
| 128 | public: |
| 129 | SendMessageValidator(IsolateGroup* isolate_group, |
| 130 | WeakTable* visited, |
| 131 | MallocGrowableArray<ObjectPtr>* const working_set) |
| 132 | : ObjectPointerVisitor(isolate_group), |
| 133 | visited_(visited), |
| 134 | working_set_(working_set) {} |
| 135 | |
| 136 | private: |
| 137 | void VisitPointers(ObjectPtr* from, ObjectPtr* to) { |
| 138 | for (ObjectPtr* raw = from; raw <= to; raw++) { |
| 139 | if (!(*raw)->IsHeapObject() || (*raw)->ptr()->IsCanonical()) { |
| 140 | continue; |
| 141 | } |
| 142 | if (visited_->GetValueExclusive(*raw) == 1) { |
| 143 | continue; |
| 144 | } |
| 145 | visited_->SetValueExclusive(*raw, 1); |
| 146 | working_set_->Add(*raw); |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | WeakTable* visited_; |
| 151 | MallocGrowableArray<ObjectPtr>* const working_set_; |
| 152 | }; |
| 153 | if (!obj.raw()->IsHeapObject() || obj.raw()->ptr()->IsCanonical()) { |
| 154 | return obj.raw(); |
| 155 | } |
| 156 | ClassTable* class_table = isolate->class_table(); |
| 157 | |
| 158 | Class& klass = Class::Handle(zone); |
| 159 | Closure& closure = Closure::Handle(zone); |
| 160 | |
| 161 | MallocGrowableArray<ObjectPtr> working_set; |
| 162 | std::unique_ptr<WeakTable> visited(new WeakTable()); |
| 163 | |
| 164 | NoSafepointScope no_safepoint; |
| 165 | SendMessageValidator visitor(isolate->group(), visited.get(), &working_set); |
| 166 | |
| 167 | visited->SetValueExclusive(obj.raw(), 1); |
| 168 | working_set.Add(obj.raw()); |
| 169 | |
| 170 | while (!working_set.is_empty()) { |
| 171 | ObjectPtr raw = working_set.RemoveLast(); |
| 172 | |
| 173 | if (visited->GetValueExclusive(raw) > 0) { |
| 174 | continue; |
| 175 | } |
| 176 | visited->SetValueExclusive(raw, 1); |
| 177 | |
| 178 | const intptr_t cid = raw->GetClassId(); |
| 179 | switch (cid) { |
| 180 | // List below matches the one in raw_object_snapshot.cc |
| 181 | #define MESSAGE_SNAPSHOT_ILLEGAL(type) \ |
| 182 | case k##type##Cid: \ |
| 183 | return Exceptions::CreateUnhandledException( \ |
| 184 | zone, Exceptions::kArgumentValue, \ |
| 185 | "Illegal argument in isolate message : (object is a " #type ")"); |
| 186 | |
| 187 | MESSAGE_SNAPSHOT_ILLEGAL(DynamicLibrary); |
| 188 | MESSAGE_SNAPSHOT_ILLEGAL(MirrorReference); |
| 189 | MESSAGE_SNAPSHOT_ILLEGAL(Pointer); |
| 190 | MESSAGE_SNAPSHOT_ILLEGAL(ReceivePort); |
| 191 | MESSAGE_SNAPSHOT_ILLEGAL(RegExp); |
| 192 | MESSAGE_SNAPSHOT_ILLEGAL(StackTrace); |
| 193 | MESSAGE_SNAPSHOT_ILLEGAL(UserTag); |
| 194 | |
| 195 | case kClosureCid: { |
| 196 | closure = Closure::RawCast(raw); |
| 197 | FunctionPtr func = closure.function(); |
| 198 | // We only allow closure of top level methods or static functions in a |
| 199 | // class to be sent in isolate messages. |
| 200 | if (!Function::IsImplicitStaticClosureFunction(func)) { |
| 201 | return Exceptions::CreateUnhandledException( |
| 202 | zone, Exceptions::kArgumentValue, "Closures are not allowed" ); |
| 203 | } |
| 204 | break; |
| 205 | } |
| 206 | default: |
| 207 | if (cid >= kNumPredefinedCids) { |
| 208 | klass = class_table->At(cid); |
| 209 | if (klass.num_native_fields() != 0) { |
| 210 | return Exceptions::CreateUnhandledException( |
| 211 | zone, Exceptions::kArgumentValue, |
| 212 | "Objects that extend NativeWrapper are not allowed" ); |
| 213 | } |
| 214 | } |
| 215 | } |
| 216 | raw->ptr()->VisitPointers(&visitor); |
| 217 | } |
| 218 | isolate->set_forward_table_new(nullptr); |
| 219 | return obj.raw(); |
| 220 | } |
| 221 | |
| 222 | DEFINE_NATIVE_ENTRY(SendPortImpl_sendAndExitInternal_, 0, 2) { |
| 223 | GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0)); |
| 224 | if (!PortMap::IsReceiverInThisIsolateGroup(port.Id(), isolate->group())) { |
| 225 | const auto& error = |
| 226 | String::Handle(String::New("sendAndExit is only supported across " |
| 227 | "isolates spawned via spawnFunction." )); |
| 228 | Exceptions::ThrowArgumentError(error); |
| 229 | UNREACHABLE(); |
| 230 | } |
| 231 | |
| 232 | GET_NON_NULL_NATIVE_ARGUMENT(Instance, obj, arguments->NativeArgAt(1)); |
| 233 | |
| 234 | Object& validated_result = Object::Handle(zone); |
| 235 | Object& msg_obj = Object::Handle(zone, obj.raw()); |
| 236 | validated_result = ValidateMessageObject(zone, isolate, msg_obj); |
| 237 | if (validated_result.IsUnhandledException()) { |
| 238 | Exceptions::PropagateError(Error::Cast(validated_result)); |
| 239 | UNREACHABLE(); |
| 240 | } |
| 241 | PersistentHandle* handle = |
| 242 | isolate->group()->api_state()->AllocatePersistentHandle(); |
| 243 | handle->set_raw(msg_obj); |
| 244 | isolate->bequeath(std::unique_ptr<Bequest>(new Bequest(handle, port.Id()))); |
| 245 | // TODO(aam): Ensure there are no dart api calls after this point as we want |
| 246 | // to ensure that validated message won't get tampered with. |
| 247 | Isolate::KillIfExists(isolate, Isolate::LibMsgId::kKillMsg); |
| 248 | // Drain interrupts before running so any IMMEDIATE operations on the current |
| 249 | // isolate happen synchronously. |
| 250 | const Error& error = Error::Handle(thread->HandleInterrupts()); |
| 251 | RELEASE_ASSERT(error.IsUnwindError()); |
| 252 | Exceptions::PropagateError(error); |
| 253 | // We will never execute dart code again in this isolate. |
| 254 | return Object::null(); |
| 255 | } |
| 256 | |
| 257 | static void ThrowIsolateSpawnException(const String& message) { |
| 258 | const Array& args = Array::Handle(Array::New(1)); |
| 259 | args.SetAt(0, message); |
| 260 | Exceptions::ThrowByType(Exceptions::kIsolateSpawn, args); |
| 261 | } |
| 262 | |
| 263 | class SpawnIsolateTask : public ThreadPool::Task { |
| 264 | public: |
| 265 | SpawnIsolateTask(Isolate* parent_isolate, |
| 266 | std::unique_ptr<IsolateSpawnState> state, |
| 267 | bool in_new_isolate_group) |
| 268 | : parent_isolate_(parent_isolate), |
| 269 | state_(std::move(state)), |
| 270 | in_new_isolate_group_(in_new_isolate_group) { |
| 271 | parent_isolate->IncrementSpawnCount(); |
| 272 | } |
| 273 | |
| 274 | ~SpawnIsolateTask() override { |
| 275 | if (parent_isolate_ != nullptr) { |
| 276 | parent_isolate_->DecrementSpawnCount(); |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | void Run() override { |
| 281 | auto group = state_->isolate_group(); |
| 282 | |
| 283 | // The create isolate group call back is mandatory. If not provided we |
| 284 | // cannot spawn isolates. |
| 285 | Dart_IsolateGroupCreateCallback create_group_callback = |
| 286 | Isolate::CreateGroupCallback(); |
| 287 | if (create_group_callback == nullptr) { |
| 288 | FailedSpawn("Isolate spawn is not supported by this Dart embedder\n" ); |
| 289 | return; |
| 290 | } |
| 291 | |
| 292 | // The initialize callback is optional atm, we fall back to creating isolate |
| 293 | // groups if it was not provided. |
| 294 | Dart_InitializeIsolateCallback initialize_callback = |
| 295 | Isolate::InitializeCallback(); |
| 296 | |
| 297 | const char* name = (state_->debug_name() == NULL) ? state_->function_name() |
| 298 | : state_->debug_name(); |
| 299 | ASSERT(name != NULL); |
| 300 | |
| 301 | // Create a new isolate. |
| 302 | char* error = nullptr; |
| 303 | Isolate* isolate = nullptr; |
| 304 | if (!FLAG_enable_isolate_groups || group == nullptr || |
| 305 | initialize_callback == nullptr || in_new_isolate_group_) { |
| 306 | // Make a copy of the state's isolate flags and hand it to the callback. |
| 307 | Dart_IsolateFlags api_flags = *(state_->isolate_flags()); |
| 308 | isolate = reinterpret_cast<Isolate*>((create_group_callback)( |
| 309 | state_->script_url(), name, nullptr, state_->package_config(), |
| 310 | &api_flags, parent_isolate_->init_callback_data(), &error)); |
| 311 | parent_isolate_->DecrementSpawnCount(); |
| 312 | parent_isolate_ = nullptr; |
| 313 | } else { |
| 314 | if (initialize_callback == nullptr) { |
| 315 | FailedSpawn("Isolate spawn is not supported by this embedder." ); |
| 316 | return; |
| 317 | } |
| 318 | |
| 319 | #if defined(DART_PRECOMPILED_RUNTIME) |
| 320 | isolate = CreateWithinExistingIsolateGroupAOT(group, name, &error); |
| 321 | #else |
| 322 | isolate = CreateWithinExistingIsolateGroup(group, name, &error); |
| 323 | #endif |
| 324 | parent_isolate_->DecrementSpawnCount(); |
| 325 | parent_isolate_ = nullptr; |
| 326 | if (isolate == nullptr) { |
| 327 | FailedSpawn(error); |
| 328 | free(error); |
| 329 | return; |
| 330 | } |
| 331 | |
| 332 | void* child_isolate_data = nullptr; |
| 333 | bool success = initialize_callback(&child_isolate_data, &error); |
| 334 | isolate->set_init_callback_data(child_isolate_data); |
| 335 | if (!success) { |
| 336 | Dart_ShutdownIsolate(); |
| 337 | FailedSpawn(error); |
| 338 | free(error); |
| 339 | return; |
| 340 | } |
| 341 | Dart_ExitIsolate(); |
| 342 | } |
| 343 | |
| 344 | if (isolate == nullptr) { |
| 345 | FailedSpawn(error); |
| 346 | free(error); |
| 347 | return; |
| 348 | } |
| 349 | |
| 350 | if (state_->origin_id() != ILLEGAL_PORT) { |
| 351 | // For isolates spawned using spawnFunction we set the origin_id |
| 352 | // to the origin_id of the parent isolate. |
| 353 | isolate->set_origin_id(state_->origin_id()); |
| 354 | } |
| 355 | MutexLocker ml(isolate->mutex()); |
| 356 | state_->set_isolate(isolate); |
| 357 | isolate->set_spawn_state(std::move(state_)); |
| 358 | if (isolate->is_runnable()) { |
| 359 | isolate->Run(); |
| 360 | } |
| 361 | } |
| 362 | |
| 363 | private: |
| 364 | void FailedSpawn(const char* error) { |
| 365 | ReportError(error != nullptr |
| 366 | ? error |
| 367 | : "Unknown error occured during Isolate spawning." ); |
| 368 | state_ = nullptr; |
| 369 | } |
| 370 | |
| 371 | void ReportError(const char* error) { |
| 372 | Dart_CObject error_cobj; |
| 373 | error_cobj.type = Dart_CObject_kString; |
| 374 | error_cobj.value.as_string = const_cast<char*>(error); |
| 375 | if (!Dart_PostCObject(state_->parent_port(), &error_cobj)) { |
| 376 | // Perhaps the parent isolate died or closed the port before we |
| 377 | // could report the error. Ignore. |
| 378 | } |
| 379 | } |
| 380 | |
| 381 | Isolate* parent_isolate_; |
| 382 | std::unique_ptr<IsolateSpawnState> state_; |
| 383 | bool in_new_isolate_group_; |
| 384 | |
| 385 | DISALLOW_COPY_AND_ASSIGN(SpawnIsolateTask); |
| 386 | }; |
| 387 | |
| 388 | static const char* String2UTF8(const String& str) { |
| 389 | intptr_t len = Utf8::Length(str); |
| 390 | char* result = new char[len + 1]; |
| 391 | str.ToUTF8(reinterpret_cast<uint8_t*>(result), len); |
| 392 | result[len] = 0; |
| 393 | |
| 394 | return result; |
| 395 | } |
| 396 | |
| 397 | DEFINE_NATIVE_ENTRY(Isolate_spawnFunction, 0, 11) { |
| 398 | GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0)); |
| 399 | GET_NON_NULL_NATIVE_ARGUMENT(String, script_uri, arguments->NativeArgAt(1)); |
| 400 | GET_NON_NULL_NATIVE_ARGUMENT(Instance, closure, arguments->NativeArgAt(2)); |
| 401 | GET_NON_NULL_NATIVE_ARGUMENT(Instance, message, arguments->NativeArgAt(3)); |
| 402 | GET_NON_NULL_NATIVE_ARGUMENT(Bool, paused, arguments->NativeArgAt(4)); |
| 403 | GET_NATIVE_ARGUMENT(Bool, fatalErrors, arguments->NativeArgAt(5)); |
| 404 | GET_NATIVE_ARGUMENT(SendPort, onExit, arguments->NativeArgAt(6)); |
| 405 | GET_NATIVE_ARGUMENT(SendPort, onError, arguments->NativeArgAt(7)); |
| 406 | GET_NATIVE_ARGUMENT(String, packageConfig, arguments->NativeArgAt(8)); |
| 407 | GET_NATIVE_ARGUMENT(Bool, newIsolateGroup, arguments->NativeArgAt(9)); |
| 408 | GET_NATIVE_ARGUMENT(String, debugName, arguments->NativeArgAt(10)); |
| 409 | |
| 410 | if (closure.IsClosure()) { |
| 411 | Function& func = Function::Handle(); |
| 412 | func = Closure::Cast(closure).function(); |
| 413 | if (func.IsImplicitClosureFunction() && func.is_static()) { |
| 414 | #if defined(DEBUG) |
| 415 | Context& ctx = Context::Handle(); |
| 416 | ctx = Closure::Cast(closure).context(); |
| 417 | ASSERT(ctx.IsNull()); |
| 418 | #endif |
| 419 | // Get the parent function so that we get the right function name. |
| 420 | func = func.parent_function(); |
| 421 | |
| 422 | bool fatal_errors = fatalErrors.IsNull() ? true : fatalErrors.value(); |
| 423 | Dart_Port on_exit_port = onExit.IsNull() ? ILLEGAL_PORT : onExit.Id(); |
| 424 | Dart_Port on_error_port = onError.IsNull() ? ILLEGAL_PORT : onError.Id(); |
| 425 | |
| 426 | // We first try to serialize the message. In case the message is not |
| 427 | // serializable this will throw an exception. |
| 428 | SerializedObjectBuffer message_buffer; |
| 429 | { |
| 430 | MessageWriter writer(/* can_send_any_object = */ true); |
| 431 | message_buffer.set_message(writer.WriteMessage( |
| 432 | message, ILLEGAL_PORT, Message::kNormalPriority)); |
| 433 | } |
| 434 | |
| 435 | const char* utf8_package_config = |
| 436 | packageConfig.IsNull() ? NULL : String2UTF8(packageConfig); |
| 437 | const char* utf8_debug_name = |
| 438 | debugName.IsNull() ? NULL : String2UTF8(debugName); |
| 439 | |
| 440 | std::unique_ptr<IsolateSpawnState> state(new IsolateSpawnState( |
| 441 | port.Id(), isolate->origin_id(), String2UTF8(script_uri), func, |
| 442 | &message_buffer, utf8_package_config, paused.value(), fatal_errors, |
| 443 | on_exit_port, on_error_port, utf8_debug_name, isolate->group())); |
| 444 | |
| 445 | // Since this is a call to Isolate.spawn, copy the parent isolate's code. |
| 446 | state->isolate_flags()->copy_parent_code = true; |
| 447 | |
| 448 | const bool in_new_isolate_group = newIsolateGroup.value(); |
| 449 | isolate->group()->thread_pool()->Run<SpawnIsolateTask>( |
| 450 | isolate, std::move(state), in_new_isolate_group); |
| 451 | return Object::null(); |
| 452 | } |
| 453 | } |
| 454 | const String& msg = String::Handle(String::New( |
| 455 | "Isolate.spawn expects to be passed a static or top-level function" )); |
| 456 | Exceptions::ThrowArgumentError(msg); |
| 457 | return Object::null(); |
| 458 | } |
| 459 | |
| 460 | static const char* CanonicalizeUri(Thread* thread, |
| 461 | const Library& library, |
| 462 | const String& uri, |
| 463 | char** error) { |
| 464 | const char* result = NULL; |
| 465 | Zone* zone = thread->zone(); |
| 466 | Isolate* isolate = thread->isolate(); |
| 467 | if (isolate->HasTagHandler()) { |
| 468 | const Object& obj = Object::Handle( |
| 469 | isolate->CallTagHandler(Dart_kCanonicalizeUrl, library, uri)); |
| 470 | if (obj.IsString()) { |
| 471 | result = String2UTF8(String::Cast(obj)); |
| 472 | } else if (obj.IsError()) { |
| 473 | Error& error_obj = Error::Handle(); |
| 474 | error_obj ^= obj.raw(); |
| 475 | *error = zone->PrintToString("Unable to canonicalize uri '%s': %s" , |
| 476 | uri.ToCString(), error_obj.ToErrorCString()); |
| 477 | } else { |
| 478 | *error = zone->PrintToString( |
| 479 | "Unable to canonicalize uri '%s': " |
| 480 | "library tag handler returned wrong type" , |
| 481 | uri.ToCString()); |
| 482 | } |
| 483 | } else { |
| 484 | *error = zone->PrintToString( |
| 485 | "Unable to canonicalize uri '%s': no library tag handler found." , |
| 486 | uri.ToCString()); |
| 487 | } |
| 488 | return result; |
| 489 | } |
| 490 | |
| 491 | DEFINE_NATIVE_ENTRY(Isolate_spawnUri, 0, 12) { |
| 492 | GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0)); |
| 493 | GET_NON_NULL_NATIVE_ARGUMENT(String, uri, arguments->NativeArgAt(1)); |
| 494 | GET_NON_NULL_NATIVE_ARGUMENT(Instance, args, arguments->NativeArgAt(2)); |
| 495 | GET_NON_NULL_NATIVE_ARGUMENT(Instance, message, arguments->NativeArgAt(3)); |
| 496 | GET_NON_NULL_NATIVE_ARGUMENT(Bool, paused, arguments->NativeArgAt(4)); |
| 497 | GET_NATIVE_ARGUMENT(SendPort, onExit, arguments->NativeArgAt(5)); |
| 498 | GET_NATIVE_ARGUMENT(SendPort, onError, arguments->NativeArgAt(6)); |
| 499 | GET_NATIVE_ARGUMENT(Bool, fatalErrors, arguments->NativeArgAt(7)); |
| 500 | GET_NATIVE_ARGUMENT(Bool, checked, arguments->NativeArgAt(8)); |
| 501 | GET_NATIVE_ARGUMENT(Array, environment, arguments->NativeArgAt(9)); |
| 502 | GET_NATIVE_ARGUMENT(String, packageConfig, arguments->NativeArgAt(10)); |
| 503 | GET_NATIVE_ARGUMENT(String, debugName, arguments->NativeArgAt(11)); |
| 504 | |
| 505 | bool fatal_errors = fatalErrors.IsNull() ? true : fatalErrors.value(); |
| 506 | Dart_Port on_exit_port = onExit.IsNull() ? ILLEGAL_PORT : onExit.Id(); |
| 507 | Dart_Port on_error_port = onError.IsNull() ? ILLEGAL_PORT : onError.Id(); |
| 508 | |
| 509 | // We first try to serialize the arguments and the message. In case the |
| 510 | // arguments or the message are not serializable this will throw an exception. |
| 511 | SerializedObjectBuffer arguments_buffer; |
| 512 | SerializedObjectBuffer message_buffer; |
| 513 | { |
| 514 | MessageWriter writer(/* can_send_any_object = */ false); |
| 515 | arguments_buffer.set_message( |
| 516 | writer.WriteMessage(args, ILLEGAL_PORT, Message::kNormalPriority)); |
| 517 | } |
| 518 | { |
| 519 | MessageWriter writer(/* can_send_any_object = */ false); |
| 520 | message_buffer.set_message( |
| 521 | writer.WriteMessage(message, ILLEGAL_PORT, Message::kNormalPriority)); |
| 522 | } |
| 523 | |
| 524 | // Canonicalize the uri with respect to the current isolate. |
| 525 | const Library& root_lib = |
| 526 | Library::Handle(isolate->object_store()->root_library()); |
| 527 | char* error = NULL; |
| 528 | const char* canonical_uri = CanonicalizeUri(thread, root_lib, uri, &error); |
| 529 | if (canonical_uri == NULL) { |
| 530 | const String& msg = String::Handle(String::New(error)); |
| 531 | ThrowIsolateSpawnException(msg); |
| 532 | } |
| 533 | |
| 534 | const char* utf8_package_config = |
| 535 | packageConfig.IsNull() ? NULL : String2UTF8(packageConfig); |
| 536 | const char* utf8_debug_name = |
| 537 | debugName.IsNull() ? NULL : String2UTF8(debugName); |
| 538 | |
| 539 | std::unique_ptr<IsolateSpawnState> state(new IsolateSpawnState( |
| 540 | port.Id(), canonical_uri, utf8_package_config, &arguments_buffer, |
| 541 | &message_buffer, paused.value(), fatal_errors, on_exit_port, |
| 542 | on_error_port, utf8_debug_name, /*group=*/nullptr)); |
| 543 | |
| 544 | // If we were passed a value then override the default flags state for |
| 545 | // checked mode. |
| 546 | if (!checked.IsNull()) { |
| 547 | Dart_IsolateFlags* flags = state->isolate_flags(); |
| 548 | flags->enable_asserts = checked.value(); |
| 549 | } |
| 550 | |
| 551 | // Since this is a call to Isolate.spawnUri, don't copy the parent's code. |
| 552 | state->isolate_flags()->copy_parent_code = false; |
| 553 | |
| 554 | const bool in_new_isolate_group = false; |
| 555 | isolate->group()->thread_pool()->Run<SpawnIsolateTask>( |
| 556 | isolate, std::move(state), in_new_isolate_group); |
| 557 | return Object::null(); |
| 558 | } |
| 559 | |
| 560 | DEFINE_NATIVE_ENTRY(Isolate_getDebugName, 0, 1) { |
| 561 | GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0)); |
| 562 | auto name = Isolate::LookupIsolateNameByPort(port.Id()); |
| 563 | if (name == nullptr) { |
| 564 | return String::null(); |
| 565 | } |
| 566 | return String::New(name.get()); |
| 567 | } |
| 568 | |
| 569 | DEFINE_NATIVE_ENTRY(Isolate_getPortAndCapabilitiesOfCurrentIsolate, 0, 0) { |
| 570 | const Array& result = Array::Handle(Array::New(3)); |
| 571 | result.SetAt(0, SendPort::Handle(SendPort::New(isolate->main_port()))); |
| 572 | result.SetAt( |
| 573 | 1, Capability::Handle(Capability::New(isolate->pause_capability()))); |
| 574 | result.SetAt( |
| 575 | 2, Capability::Handle(Capability::New(isolate->terminate_capability()))); |
| 576 | return result.raw(); |
| 577 | } |
| 578 | |
| 579 | DEFINE_NATIVE_ENTRY(Isolate_getCurrentRootUriStr, 0, 0) { |
| 580 | const Library& root_lib = |
| 581 | Library::Handle(zone, isolate->object_store()->root_library()); |
| 582 | return root_lib.url(); |
| 583 | } |
| 584 | |
| 585 | DEFINE_NATIVE_ENTRY(Isolate_sendOOB, 0, 2) { |
| 586 | GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0)); |
| 587 | GET_NON_NULL_NATIVE_ARGUMENT(Array, msg, arguments->NativeArgAt(1)); |
| 588 | |
| 589 | // Make sure to route this request to the isolate library OOB mesage handler. |
| 590 | msg.SetAt(0, Smi::Handle(Smi::New(Message::kIsolateLibOOBMsg))); |
| 591 | |
| 592 | MessageWriter writer(false); |
| 593 | PortMap::PostMessage( |
| 594 | writer.WriteMessage(msg, port.Id(), Message::kOOBPriority)); |
| 595 | |
| 596 | // Drain interrupts before running so any IMMEDIATE operations on the current |
| 597 | // isolate happen synchronously. |
| 598 | const Error& error = Error::Handle(thread->HandleInterrupts()); |
| 599 | if (!error.IsNull()) { |
| 600 | Exceptions::PropagateError(error); |
| 601 | UNREACHABLE(); |
| 602 | } |
| 603 | |
| 604 | return Object::null(); |
| 605 | } |
| 606 | |
| 607 | static void ExternalTypedDataFinalizer(void* isolate_callback_data, |
| 608 | void* peer) { |
| 609 | free(peer); |
| 610 | } |
| 611 | |
| 612 | static intptr_t GetTypedDataSizeOrThrow(const Instance& instance) { |
| 613 | // From the Dart side we are guaranteed that the type of [instance] is a |
| 614 | // subtype of TypedData. |
| 615 | if (instance.IsTypedDataBase()) { |
| 616 | return TypedDataBase::Cast(instance).LengthInBytes(); |
| 617 | } |
| 618 | |
| 619 | // This can happen if [instance] is `null` or an instance of a 3rd party class |
| 620 | // which implements [TypedData]. |
| 621 | Exceptions::ThrowArgumentError(instance); |
| 622 | } |
| 623 | |
| 624 | DEFINE_NATIVE_ENTRY(TransferableTypedData_factory, 0, 2) { |
| 625 | ASSERT( |
| 626 | TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull()); |
| 627 | |
| 628 | GET_NON_NULL_NATIVE_ARGUMENT(Instance, array_instance, |
| 629 | arguments->NativeArgAt(1)); |
| 630 | |
| 631 | Array& array = Array::Handle(); |
| 632 | intptr_t array_length; |
| 633 | if (array_instance.IsGrowableObjectArray()) { |
| 634 | const auto& growable_array = GrowableObjectArray::Cast(array_instance); |
| 635 | array ^= growable_array.data(); |
| 636 | array_length = growable_array.Length(); |
| 637 | } else if (array_instance.IsArray()) { |
| 638 | array ^= Array::Cast(array_instance).raw(); |
| 639 | array_length = array.Length(); |
| 640 | } else { |
| 641 | Exceptions::ThrowArgumentError(array_instance); |
| 642 | UNREACHABLE(); |
| 643 | } |
| 644 | Instance& instance = Instance::Handle(); |
| 645 | uint64_t total_bytes = 0; |
| 646 | const uint64_t kMaxBytes = TypedData::MaxElements(kTypedDataUint8ArrayCid); |
| 647 | for (intptr_t i = 0; i < array_length; i++) { |
| 648 | instance ^= array.At(i); |
| 649 | total_bytes += static_cast<uintptr_t>(GetTypedDataSizeOrThrow(instance)); |
| 650 | if (total_bytes > kMaxBytes) { |
| 651 | const Array& error_args = Array::Handle(Array::New(3)); |
| 652 | error_args.SetAt(0, array); |
| 653 | error_args.SetAt(1, String::Handle(String::New("data" ))); |
| 654 | error_args.SetAt( |
| 655 | 2, String::Handle(String::NewFormatted( |
| 656 | "Aggregated list exceeds max size %" Pu64 "" , kMaxBytes))); |
| 657 | Exceptions::ThrowByType(Exceptions::kArgumentValue, error_args); |
| 658 | UNREACHABLE(); |
| 659 | } |
| 660 | } |
| 661 | |
| 662 | uint8_t* data = reinterpret_cast<uint8_t*>(malloc(total_bytes)); |
| 663 | if (data == nullptr) { |
| 664 | const Instance& exception = |
| 665 | Instance::Handle(thread->isolate()->object_store()->out_of_memory()); |
| 666 | Exceptions::Throw(thread, exception); |
| 667 | UNREACHABLE(); |
| 668 | } |
| 669 | intptr_t offset = 0; |
| 670 | for (intptr_t i = 0; i < array_length; i++) { |
| 671 | instance ^= array.At(i); |
| 672 | |
| 673 | { |
| 674 | NoSafepointScope no_safepoint; |
| 675 | const auto& typed_data = TypedDataBase::Cast(instance); |
| 676 | const intptr_t length_in_bytes = typed_data.LengthInBytes(); |
| 677 | |
| 678 | void* source = typed_data.DataAddr(0); |
| 679 | // The memory does not overlap. |
| 680 | memcpy(data + offset, source, length_in_bytes); // NOLINT |
| 681 | offset += length_in_bytes; |
| 682 | } |
| 683 | } |
| 684 | ASSERT(static_cast<uintptr_t>(offset) == total_bytes); |
| 685 | return TransferableTypedData::New(data, total_bytes); |
| 686 | } |
| 687 | |
| 688 | DEFINE_NATIVE_ENTRY(TransferableTypedData_materialize, 0, 1) { |
| 689 | GET_NON_NULL_NATIVE_ARGUMENT(TransferableTypedData, t, |
| 690 | arguments->NativeArgAt(0)); |
| 691 | |
| 692 | void* peer; |
| 693 | { |
| 694 | NoSafepointScope no_safepoint; |
| 695 | peer = thread->heap()->GetPeer(t.raw()); |
| 696 | // Assume that object's Peer is only used to track transferrability state. |
| 697 | ASSERT(peer != nullptr); |
| 698 | } |
| 699 | |
| 700 | TransferableTypedDataPeer* tpeer = |
| 701 | reinterpret_cast<TransferableTypedDataPeer*>(peer); |
| 702 | const intptr_t length = tpeer->length(); |
| 703 | uint8_t* data = tpeer->data(); |
| 704 | if (data == nullptr) { |
| 705 | const auto& error = String::Handle(String::New( |
| 706 | "Attempt to materialize object that was transferred already." )); |
| 707 | Exceptions::ThrowArgumentError(error); |
| 708 | UNREACHABLE(); |
| 709 | } |
| 710 | tpeer->ClearData(); |
| 711 | |
| 712 | const ExternalTypedData& typed_data = ExternalTypedData::Handle( |
| 713 | ExternalTypedData::New(kExternalTypedDataUint8ArrayCid, data, length, |
| 714 | thread->heap()->SpaceForExternal(length))); |
| 715 | FinalizablePersistentHandle::New(thread->isolate(), typed_data, |
| 716 | /* peer= */ data, |
| 717 | &ExternalTypedDataFinalizer, length, |
| 718 | /*auto_delete=*/true); |
| 719 | return typed_data.raw(); |
| 720 | } |
| 721 | |
| 722 | } // namespace dart |
| 723 | |