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 | |