| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #include "include/dart_api.h" |
| #include "include/dart_native_api.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "lib/stacktrace.h" |
| #include "platform/assert.h" |
| #include "platform/unicode.h" |
| #include "vm/class_finalizer.h" |
| #include "vm/clustered_snapshot.h" |
| #include "vm/compiler/jit/compiler.h" |
| #include "vm/dart.h" |
| #include "vm/dart_api_impl.h" |
| #include "vm/dart_api_message.h" |
| #include "vm/dart_api_state.h" |
| #include "vm/dart_entry.h" |
| #include "vm/debugger.h" |
| #include "vm/dwarf.h" |
| #include "vm/elf.h" |
| #include "vm/exceptions.h" |
| #include "vm/flags.h" |
| #include "vm/growable_array.h" |
| #include "vm/heap/verifier.h" |
| #include "vm/image_snapshot.h" |
| #include "vm/isolate_reload.h" |
| #include "vm/kernel_isolate.h" |
| #include "vm/lockers.h" |
| #include "vm/message.h" |
| #include "vm/message_handler.h" |
| #include "vm/native_entry.h" |
| #include "vm/native_symbol.h" |
| #include "vm/object.h" |
| #include "vm/object_store.h" |
| #include "vm/os.h" |
| #include "vm/os_thread.h" |
| #include "vm/port.h" |
| #include "vm/profiler.h" |
| #include "vm/profiler_service.h" |
| #include "vm/program_visitor.h" |
| #include "vm/resolver.h" |
| #include "vm/reusable_handles.h" |
| #include "vm/service.h" |
| #include "vm/service_event.h" |
| #include "vm/service_isolate.h" |
| #include "vm/stack_frame.h" |
| #include "vm/symbols.h" |
| #include "vm/tags.h" |
| #include "vm/thread_registry.h" |
| #include "vm/uri.h" |
| #include "vm/version.h" |
| |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| #include "vm/compiler/aot/precompiler.h" |
| #include "vm/kernel_loader.h" |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| |
| namespace dart { |
| |
| // Facilitate quick access to the current zone once we have the current thread. |
| #define Z (T->zone()) |
| |
| DECLARE_FLAG(bool, print_class_table); |
| DECLARE_FLAG(bool, verify_handles); |
| #if defined(DEBUG) && !defined(DART_PRECOMPILED_RUNTIME) |
| DEFINE_FLAG(bool, |
| check_function_fingerprints, |
| true, |
| "Check function fingerprints"); |
| #endif // defined(DEBUG) && !defined(DART_PRECOMPILED_RUNTIME). |
| DEFINE_FLAG(bool, |
| verify_acquired_data, |
| false, |
| "Verify correct API acquire/release of typed data."); |
| DEFINE_FLAG(bool, |
| dump_tables, |
| false, |
| "Dump common hash tables before snapshotting."); |
| |
| #define CHECK_ERROR_HANDLE(error) \ |
| { \ |
| ErrorPtr err = (error); \ |
| if (err != Error::null()) { \ |
| return Api::NewHandle(T, err); \ |
| } \ |
| } |
| |
| ThreadLocalKey Api::api_native_key_ = kUnsetThreadLocalKey; |
| Dart_Handle Api::true_handle_ = NULL; |
| Dart_Handle Api::false_handle_ = NULL; |
| Dart_Handle Api::null_handle_ = NULL; |
| Dart_Handle Api::empty_string_handle_ = NULL; |
| |
| const char* CanonicalFunction(const char* func) { |
| if (strncmp(func, "dart::", 6) == 0) { |
| return func + 6; |
| } else { |
| return func; |
| } |
| } |
| |
| #if defined(DEBUG) |
| // An object visitor which will iterate over all the function objects in the |
| // heap and check if the result type and parameter types are canonicalized |
| // or not. An assertion is raised if a type is not canonicalized. |
| class CheckFunctionTypesVisitor : public ObjectVisitor { |
| public: |
| explicit CheckFunctionTypesVisitor(Thread* thread) |
| : classHandle_(Class::Handle(thread->zone())), |
| funcHandle_(Function::Handle(thread->zone())), |
| typeHandle_(AbstractType::Handle(thread->zone())) {} |
| |
| void VisitObject(ObjectPtr obj) { |
| if (obj->IsFunction()) { |
| funcHandle_ ^= obj; |
| classHandle_ ^= funcHandle_.Owner(); |
| // Verify that the result type of a function is canonical or a |
| // TypeParameter. |
| typeHandle_ ^= funcHandle_.result_type(); |
| ASSERT(typeHandle_.IsTypeParameter() || typeHandle_.IsCanonical()); |
| // Verify that the types in the function signature are all canonical or |
| // a TypeParameter. |
| const intptr_t num_parameters = funcHandle_.NumParameters(); |
| for (intptr_t i = 0; i < num_parameters; i++) { |
| typeHandle_ = funcHandle_.ParameterTypeAt(i); |
| ASSERT(typeHandle_.IsTypeParameter() || typeHandle_.IsCanonical()); |
| } |
| } |
| } |
| |
| private: |
| Class& classHandle_; |
| Function& funcHandle_; |
| AbstractType& typeHandle_; |
| }; |
| #endif // #if defined(DEBUG). |
| |
| static InstancePtr GetListInstance(Zone* zone, const Object& obj) { |
| if (obj.IsInstance()) { |
| ObjectStore* object_store = IsolateGroup::Current()->object_store(); |
| const Type& list_rare_type = |
| Type::Handle(zone, object_store->non_nullable_list_rare_type()); |
| ASSERT(!list_rare_type.IsNull()); |
| const Instance& instance = Instance::Cast(obj); |
| const Class& obj_class = Class::Handle(zone, obj.clazz()); |
| if (Class::IsSubtypeOf(obj_class, Object::null_type_arguments(), |
| Nullability::kNonNullable, list_rare_type, |
| Heap::kNew)) { |
| return instance.ptr(); |
| } |
| } |
| return Instance::null(); |
| } |
| |
| static InstancePtr GetMapInstance(Zone* zone, const Object& obj) { |
| if (obj.IsInstance()) { |
| ObjectStore* object_store = IsolateGroup::Current()->object_store(); |
| const Type& map_rare_type = |
| Type::Handle(zone, object_store->non_nullable_map_rare_type()); |
| ASSERT(!map_rare_type.IsNull()); |
| const Instance& instance = Instance::Cast(obj); |
| const Class& obj_class = Class::Handle(zone, obj.clazz()); |
| if (Class::IsSubtypeOf(obj_class, Object::null_type_arguments(), |
| Nullability::kNonNullable, map_rare_type, |
| Heap::kNew)) { |
| return instance.ptr(); |
| } |
| } |
| return Instance::null(); |
| } |
| |
| static bool IsCompiletimeErrorObject(Zone* zone, const Object& obj) { |
| #if defined(DART_PRECOMPILED_RUNTIME) |
| // All compile-time errors were handled at snapshot generation time and |
| // compiletime_error_class was removed. |
| return false; |
| #else |
| auto isolate_group = Thread::Current()->isolate_group(); |
| const Class& error_class = Class::Handle( |
| zone, isolate_group->object_store()->compiletime_error_class()); |
| ASSERT(!error_class.IsNull()); |
| return (obj.GetClassId() == error_class.id()); |
| #endif |
| } |
| |
| static bool GetNativeStringArgument(NativeArguments* arguments, |
| int arg_index, |
| Dart_Handle* str, |
| void** peer) { |
| ASSERT(peer != NULL); |
| if (Api::StringGetPeerHelper(arguments, arg_index, peer)) { |
| *str = NULL; |
| return true; |
| } |
| Thread* thread = arguments->thread(); |
| ASSERT(thread == Thread::Current()); |
| *peer = NULL; |
| REUSABLE_OBJECT_HANDLESCOPE(thread); |
| Object& obj = thread->ObjectHandle(); |
| obj = arguments->NativeArgAt(arg_index); |
| if (IsStringClassId(obj.GetClassId())) { |
| ASSERT(thread->api_top_scope() != NULL); |
| *str = Api::NewHandle(thread, obj.ptr()); |
| return true; |
| } |
| if (obj.IsNull()) { |
| *str = Api::Null(); |
| return true; |
| } |
| return false; |
| } |
| |
| static bool GetNativeIntegerArgument(NativeArguments* arguments, |
| int arg_index, |
| int64_t* value) { |
| ASSERT(value != NULL); |
| return Api::GetNativeIntegerArgument(arguments, arg_index, value); |
| } |
| |
| static bool GetNativeUnsignedIntegerArgument(NativeArguments* arguments, |
| int arg_index, |
| uint64_t* value) { |
| ASSERT(value != NULL); |
| int64_t arg_value = 0; |
| if (Api::GetNativeIntegerArgument(arguments, arg_index, &arg_value)) { |
| *value = static_cast<uint64_t>(arg_value); |
| return true; |
| } |
| return false; |
| } |
| |
| static bool GetNativeDoubleArgument(NativeArguments* arguments, |
| int arg_index, |
| double* value) { |
| ASSERT(value != NULL); |
| return Api::GetNativeDoubleArgument(arguments, arg_index, value); |
| } |
| |
| static Dart_Handle GetNativeFieldsOfArgument(NativeArguments* arguments, |
| int arg_index, |
| int num_fields, |
| intptr_t* field_values, |
| const char* current_func) { |
| ASSERT(field_values != NULL); |
| if (Api::GetNativeFieldsOfArgument(arguments, arg_index, num_fields, |
| field_values)) { |
| return Api::Success(); |
| } |
| Thread* thread = arguments->thread(); |
| ASSERT(thread == Thread::Current()); |
| REUSABLE_OBJECT_HANDLESCOPE(thread); |
| Object& obj = thread->ObjectHandle(); |
| obj = arguments->NativeArgAt(arg_index); |
| if (obj.IsNull()) { |
| memset(field_values, 0, (num_fields * sizeof(field_values[0]))); |
| return Api::Success(); |
| } |
| // We did not succeed in extracting the native fields report the |
| // appropriate error. |
| if (!obj.IsInstance()) { |
| return Api::NewError( |
| "%s expects argument at index '%d' to be of" |
| " type Instance.", |
| current_func, arg_index); |
| } |
| const Instance& instance = Instance::Cast(obj); |
| int field_count = instance.NumNativeFields(); |
| ASSERT(num_fields != field_count); |
| return Api::NewError("%s: expected %d 'num_fields' but was passed in %d.", |
| current_func, field_count, num_fields); |
| } |
| |
| static FunctionPtr FindCoreLibPrivateFunction(Zone* zone, const String& name) { |
| const Library& core_lib = Library::Handle(zone, Library::CoreLibrary()); |
| ASSERT(!core_lib.IsNull()); |
| const Function& function = |
| Function::Handle(zone, core_lib.LookupFunctionAllowPrivate(name)); |
| ASSERT(!function.IsNull()); |
| return function.ptr(); |
| } |
| |
| static ObjectPtr CallStatic1Arg(Zone* zone, |
| const String& name, |
| const Instance& arg0) { |
| const intptr_t kNumArgs = 1; |
| const Function& function = |
| Function::Handle(zone, FindCoreLibPrivateFunction(zone, name)); |
| const Array& args = Array::Handle(zone, Array::New(kNumArgs)); |
| args.SetAt(0, arg0); |
| return DartEntry::InvokeFunction(function, args); |
| } |
| |
| static ObjectPtr CallStatic2Args(Zone* zone, |
| const String& name, |
| const Instance& arg0, |
| const Instance& arg1) { |
| const intptr_t kNumArgs = 2; |
| const Function& function = |
| Function::Handle(zone, FindCoreLibPrivateFunction(zone, name)); |
| const Array& args = Array::Handle(zone, Array::New(kNumArgs)); |
| args.SetAt(0, arg0); |
| args.SetAt(1, arg1); |
| return DartEntry::InvokeFunction(function, args); |
| } |
| |
| static ObjectPtr CallStatic3Args(Zone* zone, |
| const String& name, |
| const Instance& arg0, |
| const Instance& arg1, |
| const Instance& arg2) { |
| const intptr_t kNumArgs = 3; |
| const Function& function = |
| Function::Handle(zone, FindCoreLibPrivateFunction(zone, name)); |
| const Array& args = Array::Handle(Array::New(kNumArgs)); |
| args.SetAt(0, arg0); |
| args.SetAt(1, arg1); |
| args.SetAt(2, arg2); |
| return DartEntry::InvokeFunction(function, args); |
| } |
| |
| static const char* GetErrorString(Thread* thread, const Object& obj) { |
| // This function requires an API scope to be present. |
| if (obj.IsError()) { |
| ASSERT(thread->api_top_scope() != NULL); |
| const Error& error = Error::Cast(obj); |
| const char* str = error.ToErrorCString(); |
| intptr_t len = strlen(str) + 1; |
| char* str_copy = Api::TopScope(thread)->zone()->Alloc<char>(len); |
| strncpy(str_copy, str, len); |
| // Strip a possible trailing '\n'. |
| if ((len > 1) && (str_copy[len - 2] == '\n')) { |
| str_copy[len - 2] = '\0'; |
| } |
| return str_copy; |
| } else { |
| return ""; |
| } |
| } |
| |
| Dart_Handle Api::InitNewHandle(Thread* thread, ObjectPtr raw) { |
| LocalHandles* local_handles = Api::TopScope(thread)->local_handles(); |
| ASSERT(local_handles != NULL); |
| LocalHandle* ref = local_handles->AllocateHandle(); |
| ref->set_ptr(raw); |
| return ref->apiHandle(); |
| } |
| |
| Dart_Handle Api::NewHandle(Thread* thread, ObjectPtr raw) { |
| if (raw == Object::null()) { |
| return Null(); |
| } |
| if (raw == Bool::True().ptr()) { |
| return True(); |
| } |
| if (raw == Bool::False().ptr()) { |
| return False(); |
| } |
| ASSERT(thread->execution_state() == Thread::kThreadInVM); |
| return InitNewHandle(thread, raw); |
| } |
| |
| ObjectPtr Api::UnwrapHandle(Dart_Handle object) { |
| #if defined(DEBUG) |
| Thread* thread = Thread::Current(); |
| ASSERT(thread->execution_state() == Thread::kThreadInVM); |
| ASSERT(thread->IsMutatorThread()); |
| ASSERT(thread->isolate() != NULL); |
| ASSERT(!FLAG_verify_handles || thread->IsValidLocalHandle(object) || |
| thread->isolate()->group()->api_state()->IsActivePersistentHandle( |
| reinterpret_cast<Dart_PersistentHandle>(object)) || |
| Dart::IsReadOnlyApiHandle(object)); |
| ASSERT(FinalizablePersistentHandle::ptr_offset() == 0 && |
| PersistentHandle::ptr_offset() == 0 && LocalHandle::ptr_offset() == 0); |
| #endif |
| return (reinterpret_cast<LocalHandle*>(object))->ptr(); |
| } |
| |
| #define DEFINE_UNWRAP(type) \ |
| const type& Api::Unwrap##type##Handle(Zone* zone, Dart_Handle dart_handle) { \ |
| const Object& obj = Object::Handle(zone, Api::UnwrapHandle(dart_handle)); \ |
| if (obj.Is##type()) { \ |
| return type::Cast(obj); \ |
| } \ |
| return type::Handle(zone); \ |
| } |
| CLASS_LIST_FOR_HANDLES(DEFINE_UNWRAP) |
| #undef DEFINE_UNWRAP |
| |
| const String& Api::UnwrapStringHandle(const ReusableObjectHandleScope& reuse, |
| Dart_Handle dart_handle) { |
| Object& ref = reuse.Handle(); |
| ref = Api::UnwrapHandle(dart_handle); |
| if (ref.IsString()) { |
| return String::Cast(ref); |
| } |
| return Object::null_string(); |
| } |
| |
| const Instance& Api::UnwrapInstanceHandle( |
| const ReusableObjectHandleScope& reuse, |
| Dart_Handle dart_handle) { |
| Object& ref = reuse.Handle(); |
| ref = Api::UnwrapHandle(dart_handle); |
| if (ref.IsInstance()) { |
| return Instance::Cast(ref); |
| } |
| return Object::null_instance(); |
| } |
| |
| Dart_Handle Api::CheckAndFinalizePendingClasses(Thread* thread) { |
| Isolate* isolate = thread->isolate(); |
| if (!isolate->AllowClassFinalization()) { |
| // Class finalization is blocked for the isolate. Do nothing. |
| return Api::Success(); |
| } |
| if (ClassFinalizer::ProcessPendingClasses()) { |
| return Api::Success(); |
| } |
| ASSERT(thread->sticky_error() != Object::null()); |
| return Api::NewHandle(thread, thread->sticky_error()); |
| } |
| |
| Dart_Isolate Api::CastIsolate(Isolate* isolate) { |
| return reinterpret_cast<Dart_Isolate>(isolate); |
| } |
| |
| Dart_IsolateGroup Api::CastIsolateGroup(IsolateGroup* isolate_group) { |
| return reinterpret_cast<Dart_IsolateGroup>(isolate_group); |
| } |
| |
| Dart_Handle Api::NewError(const char* format, ...) { |
| Thread* T = Thread::Current(); |
| CHECK_API_SCOPE(T); |
| CHECK_CALLBACK_STATE(T); |
| // Ensure we transition safepoint state to VM if we are not already in |
| // that state. |
| TransitionToVM transition(T); |
| HANDLESCOPE(T); |
| |
| va_list args; |
| va_start(args, format); |
| char* buffer = OS::VSCreate(Z, format, args); |
| va_end(args); |
| |
| const String& message = String::Handle(Z, String::New(buffer)); |
| return Api::NewHandle(T, ApiError::New(message)); |
| } |
| |
| Dart_Handle Api::NewArgumentError(const char* format, ...) { |
| Thread* T = Thread::Current(); |
| CHECK_API_SCOPE(T); |
| CHECK_CALLBACK_STATE(T); |
| // Ensure we transition safepoint state to VM if we are not already in |
| // that state. |
| TransitionToVM transition(T); |
| HANDLESCOPE(T); |
| |
| va_list args; |
| va_start(args, format); |
| char* buffer = OS::VSCreate(Z, format, args); |
| va_end(args); |
| |
| const String& message = String::Handle(Z, String::New(buffer)); |
| const Array& arguments = Array::Handle(Z, Array::New(1)); |
| arguments.SetAt(0, message); |
| Object& error = Object::Handle( |
| Z, DartLibraryCalls::InstanceCreate( |
| Library::Handle(Z, Library::CoreLibrary()), |
| Symbols::ArgumentError(), Symbols::Dot(), arguments)); |
| if (!error.IsError()) { |
| error = UnhandledException::New(Instance::Cast(error), Instance::Handle()); |
| } |
| return Api::NewHandle(T, error.ptr()); |
| } |
| |
| Dart_Handle Api::AcquiredError(IsolateGroup* isolate_group) { |
| ApiState* state = isolate_group->api_state(); |
| ASSERT(state != NULL); |
| PersistentHandle* acquired_error_handle = state->AcquiredError(); |
| return reinterpret_cast<Dart_Handle>(acquired_error_handle); |
| } |
| |
| bool Api::IsValid(Dart_Handle handle) { |
| Isolate* isolate = Isolate::Current(); |
| Thread* thread = Thread::Current(); |
| ASSERT(thread->IsMutatorThread()); |
| CHECK_ISOLATE(isolate); |
| |
| // Check against all of the handles in the current isolate as well as the |
| // read-only handles. |
| return thread->IsValidHandle(handle) || |
| isolate->group()->api_state()->IsActivePersistentHandle( |
| reinterpret_cast<Dart_PersistentHandle>(handle)) || |
| isolate->group()->api_state()->IsActiveWeakPersistentHandle( |
| reinterpret_cast<Dart_WeakPersistentHandle>(handle)) || |
| Dart::IsReadOnlyApiHandle(handle) || |
| Dart::IsReadOnlyHandle(reinterpret_cast<uword>(handle)); |
| } |
| |
| ApiLocalScope* Api::TopScope(Thread* thread) { |
| ASSERT(thread != NULL); |
| ApiLocalScope* scope = thread->api_top_scope(); |
| ASSERT(scope != NULL); |
| return scope; |
| } |
| |
| void Api::Init() { |
| if (api_native_key_ == kUnsetThreadLocalKey) { |
| api_native_key_ = OSThread::CreateThreadLocal(); |
| } |
| ASSERT(api_native_key_ != kUnsetThreadLocalKey); |
| } |
| |
| static Dart_Handle InitNewReadOnlyApiHandle(ObjectPtr raw) { |
| ASSERT(raw->untag()->InVMIsolateHeap()); |
| LocalHandle* ref = Dart::AllocateReadOnlyApiHandle(); |
| ref->set_ptr(raw); |
| return ref->apiHandle(); |
| } |
| |
| void Api::InitHandles() { |
| Isolate* isolate = Isolate::Current(); |
| ASSERT(isolate != NULL); |
| ASSERT(isolate == Dart::vm_isolate()); |
| ApiState* state = isolate->group()->api_state(); |
| ASSERT(state != NULL); |
| |
| ASSERT(true_handle_ == NULL); |
| true_handle_ = InitNewReadOnlyApiHandle(Bool::True().ptr()); |
| |
| ASSERT(false_handle_ == NULL); |
| false_handle_ = InitNewReadOnlyApiHandle(Bool::False().ptr()); |
| |
| ASSERT(null_handle_ == NULL); |
| null_handle_ = InitNewReadOnlyApiHandle(Object::null()); |
| |
| ASSERT(empty_string_handle_ == NULL); |
| empty_string_handle_ = InitNewReadOnlyApiHandle(Symbols::Empty().ptr()); |
| } |
| |
| void Api::Cleanup() { |
| true_handle_ = NULL; |
| false_handle_ = NULL; |
| null_handle_ = NULL; |
| empty_string_handle_ = NULL; |
| } |
| |
| bool Api::StringGetPeerHelper(NativeArguments* arguments, |
| int arg_index, |
| void** peer) { |
| NoSafepointScope no_safepoint_scope; |
| ObjectPtr raw_obj = arguments->NativeArgAt(arg_index); |
| if (!raw_obj->IsHeapObject()) { |
| return false; |
| } |
| intptr_t cid = raw_obj->GetClassId(); |
| if (cid == kExternalOneByteStringCid) { |
| ExternalOneByteStringPtr raw_string = |
| static_cast<ExternalOneByteStringPtr>(raw_obj); |
| *peer = raw_string->untag()->peer_; |
| return true; |
| } |
| if (cid == kOneByteStringCid || cid == kTwoByteStringCid) { |
| auto isolate_group = arguments->thread()->isolate_group(); |
| *peer = isolate_group->heap()->GetPeer(raw_obj); |
| return (*peer != 0); |
| } |
| if (cid == kExternalTwoByteStringCid) { |
| ExternalTwoByteStringPtr raw_string = |
| static_cast<ExternalTwoByteStringPtr>(raw_obj); |
| *peer = raw_string->untag()->peer_; |
| return true; |
| } |
| return false; |
| } |
| |
| bool Api::GetNativeReceiver(NativeArguments* arguments, intptr_t* value) { |
| NoSafepointScope no_safepoint_scope; |
| ObjectPtr raw_obj = arguments->NativeArg0(); |
| if (raw_obj->IsHeapObject()) { |
| intptr_t cid = raw_obj->GetClassId(); |
| if (cid >= kNumPredefinedCids) { |
| ASSERT(Instance::Cast(Object::Handle(raw_obj)).IsValidNativeIndex(0)); |
| TypedDataPtr native_fields = *reinterpret_cast<TypedDataPtr*>( |
| UntaggedObject::ToAddr(raw_obj) + sizeof(UntaggedObject)); |
| if (native_fields == TypedData::null()) { |
| *value = 0; |
| } else { |
| *value = *bit_cast<intptr_t*, uint8_t*>(native_fields->untag()->data()); |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool Api::GetNativeBooleanArgument(NativeArguments* arguments, |
| int arg_index, |
| bool* value) { |
| NoSafepointScope no_safepoint_scope; |
| ObjectPtr raw_obj = arguments->NativeArgAt(arg_index); |
| if (raw_obj->IsHeapObject()) { |
| intptr_t cid = raw_obj->GetClassId(); |
| if (cid == kBoolCid) { |
| *value = (raw_obj == Object::bool_true().ptr()); |
| return true; |
| } |
| if (cid == kNullCid) { |
| *value = false; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool Api::GetNativeIntegerArgument(NativeArguments* arguments, |
| int arg_index, |
| int64_t* value) { |
| NoSafepointScope no_safepoint_scope; |
| ObjectPtr raw_obj = arguments->NativeArgAt(arg_index); |
| if (raw_obj->IsHeapObject()) { |
| intptr_t cid = raw_obj->GetClassId(); |
| if (cid == kMintCid) { |
| *value = static_cast<MintPtr>(raw_obj)->untag()->value_; |
| return true; |
| } |
| return false; |
| } |
| *value = Smi::Value(static_cast<SmiPtr>(raw_obj)); |
| return true; |
| } |
| |
| bool Api::GetNativeDoubleArgument(NativeArguments* arguments, |
| int arg_index, |
| double* value) { |
| NoSafepointScope no_safepoint_scope; |
| ObjectPtr raw_obj = arguments->NativeArgAt(arg_index); |
| if (raw_obj->IsHeapObject()) { |
| intptr_t cid = raw_obj->GetClassId(); |
| if (cid == kDoubleCid) { |
| *value = static_cast<DoublePtr>(raw_obj)->untag()->value_; |
| return true; |
| } |
| if (cid == kMintCid) { |
| *value = |
| static_cast<double>(static_cast<MintPtr>(raw_obj)->untag()->value_); |
| return true; |
| } |
| return false; |
| } |
| *value = static_cast<double>(Smi::Value(static_cast<SmiPtr>(raw_obj))); |
| return true; |
| } |
| |
| bool Api::GetNativeFieldsOfArgument(NativeArguments* arguments, |
| int arg_index, |
| int num_fields, |
| intptr_t* field_values) { |
| NoSafepointScope no_safepoint_scope; |
| ObjectPtr raw_obj = arguments->NativeArgAt(arg_index); |
| intptr_t cid = raw_obj->GetClassIdMayBeSmi(); |
| int class_num_fields = arguments->thread() |
| ->isolate_group() |
| ->class_table() |
| ->At(cid) |
| ->untag() |
| ->num_native_fields_; |
| if (num_fields != class_num_fields) { |
| // No native fields or mismatched native field count. |
| return false; |
| } |
| TypedDataPtr native_fields = *reinterpret_cast<TypedDataPtr*>( |
| UntaggedObject::ToAddr(raw_obj) + sizeof(UntaggedObject)); |
| if (native_fields == TypedData::null()) { |
| // Native fields not initialized. |
| memset(field_values, 0, (num_fields * sizeof(field_values[0]))); |
| return true; |
| } |
| ASSERT(class_num_fields == Smi::Value(native_fields->untag()->length())); |
| intptr_t* native_values = |
| reinterpret_cast<intptr_t*>(native_fields->untag()->data()); |
| memmove(field_values, native_values, (num_fields * sizeof(field_values[0]))); |
| return true; |
| } |
| |
| void Api::SetWeakHandleReturnValue(NativeArguments* args, |
| Dart_WeakPersistentHandle retval) { |
| args->SetReturnUnsafe(FinalizablePersistentHandle::Cast(retval)->ptr()); |
| } |
| |
| PersistentHandle* PersistentHandle::Cast(Dart_PersistentHandle handle) { |
| ASSERT(IsolateGroup::Current()->api_state()->IsValidPersistentHandle(handle)); |
| return reinterpret_cast<PersistentHandle*>(handle); |
| } |
| |
| FinalizablePersistentHandle* FinalizablePersistentHandle::Cast( |
| Dart_WeakPersistentHandle handle) { |
| #if defined(DEBUG) |
| ApiState* state = IsolateGroup::Current()->api_state(); |
| ASSERT(state->IsValidWeakPersistentHandle(handle)); |
| #endif |
| return reinterpret_cast<FinalizablePersistentHandle*>(handle); |
| } |
| FinalizablePersistentHandle* FinalizablePersistentHandle::Cast( |
| Dart_FinalizableHandle handle) { |
| #if defined(DEBUG) |
| ApiState* state = IsolateGroup::Current()->api_state(); |
| ASSERT(state->IsValidFinalizableHandle(handle)); |
| #endif |
| return reinterpret_cast<FinalizablePersistentHandle*>(handle); |
| } |
| |
| void FinalizablePersistentHandle::Finalize( |
| IsolateGroup* isolate_group, |
| FinalizablePersistentHandle* handle) { |
| if (!handle->ptr()->IsHeapObject()) { |
| return; // Free handle. |
| } |
| Dart_HandleFinalizer callback = handle->callback(); |
| ASSERT(callback != NULL); |
| void* peer = handle->peer(); |
| ApiState* state = isolate_group->api_state(); |
| ASSERT(state != NULL); |
| |
| if (!handle->auto_delete()) { |
| // Clear handle before running finalizer, finalizer can free the handle. |
| state->ClearWeakPersistentHandle(handle); |
| } |
| |
| (*callback)(isolate_group->embedder_data(), peer); |
| |
| if (handle->auto_delete()) { |
| state->FreeWeakPersistentHandle(handle); |
| } |
| } |
| |
| // --- Handles --- |
| |
| DART_EXPORT bool Dart_IsError(Dart_Handle handle) { |
| Thread* thread = Thread::Current(); |
| TransitionNativeToVM transition(thread); |
| return Api::IsError(handle); |
| } |
| |
| DART_EXPORT void Dart_KillIsolate(Dart_Isolate handle) { |
| Isolate* isolate = reinterpret_cast<Isolate*>(handle); |
| CHECK_ISOLATE(isolate); |
| Isolate::KillIfExists(isolate, Isolate::kKillMsg); |
| } |
| |
| DART_EXPORT bool Dart_IsApiError(Dart_Handle object) { |
| Thread* thread = Thread::Current(); |
| TransitionNativeToVM transition(thread); |
| return Api::ClassId(object) == kApiErrorCid; |
| } |
| |
| DART_EXPORT bool Dart_IsUnhandledExceptionError(Dart_Handle object) { |
| Thread* thread = Thread::Current(); |
| TransitionNativeToVM transition(thread); |
| return Api::ClassId(object) == kUnhandledExceptionCid; |
| } |
| |
| DART_EXPORT bool Dart_IsCompilationError(Dart_Handle object) { |
| if (::Dart_IsUnhandledExceptionError(object)) { |
| DARTSCOPE(Thread::Current()); |
| const UnhandledException& error = |
| UnhandledException::Cast(Object::Handle(Z, Api::UnwrapHandle(object))); |
| const Instance& exc = Instance::Handle(Z, error.exception()); |
| return IsCompiletimeErrorObject(Z, exc); |
| } |
| |
| Thread* thread = Thread::Current(); |
| TransitionNativeToVM transition(thread); |
| return Api::ClassId(object) == kLanguageErrorCid; |
| } |
| |
| DART_EXPORT bool Dart_IsFatalError(Dart_Handle object) { |
| Thread* thread = Thread::Current(); |
| TransitionNativeToVM transition(thread); |
| return Api::ClassId(object) == kUnwindErrorCid; |
| } |
| |
| DART_EXPORT const char* Dart_GetError(Dart_Handle handle) { |
| DARTSCOPE(Thread::Current()); |
| API_TIMELINE_DURATION(T); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(handle)); |
| return GetErrorString(T, obj); |
| } |
| |
| DART_EXPORT bool Dart_ErrorHasException(Dart_Handle handle) { |
| DARTSCOPE(Thread::Current()); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(handle)); |
| return obj.IsUnhandledException(); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_ErrorGetException(Dart_Handle handle) { |
| DARTSCOPE(Thread::Current()); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(handle)); |
| if (obj.IsUnhandledException()) { |
| const UnhandledException& error = UnhandledException::Cast(obj); |
| return Api::NewHandle(T, error.exception()); |
| } else if (obj.IsError()) { |
| return Api::NewError("This error is not an unhandled exception error."); |
| } else { |
| return Api::NewError("Can only get exceptions from error handles."); |
| } |
| } |
| |
| DART_EXPORT Dart_Handle Dart_ErrorGetStackTrace(Dart_Handle handle) { |
| DARTSCOPE(Thread::Current()); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(handle)); |
| if (obj.IsUnhandledException()) { |
| const UnhandledException& error = UnhandledException::Cast(obj); |
| return Api::NewHandle(T, error.stacktrace()); |
| } else if (obj.IsError()) { |
| return Api::NewError("This error is not an unhandled exception error."); |
| } else { |
| return Api::NewError("Can only get stacktraces from error handles."); |
| } |
| } |
| |
| DART_EXPORT Dart_Handle Dart_NewApiError(const char* error) { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| |
| const String& message = String::Handle(Z, String::New(error)); |
| return Api::NewHandle(T, ApiError::New(message)); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_NewCompilationError(const char* error) { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| |
| const String& message = String::Handle(Z, String::New(error)); |
| return Api::NewHandle(T, LanguageError::New(message)); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_NewUnhandledExceptionError(Dart_Handle exception) { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| |
| Instance& obj = Instance::Handle(Z); |
| intptr_t class_id = Api::ClassId(exception); |
| if ((class_id == kApiErrorCid) || (class_id == kLanguageErrorCid)) { |
| const Object& excp = Object::Handle(Z, Api::UnwrapHandle(exception)); |
| obj = String::New(GetErrorString(T, excp)); |
| } else { |
| obj = Api::UnwrapInstanceHandle(Z, exception).ptr(); |
| if (obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, exception, Instance); |
| } |
| } |
| const StackTrace& stacktrace = StackTrace::Handle(Z); |
| return Api::NewHandle(T, UnhandledException::New(obj, stacktrace)); |
| } |
| |
| DART_EXPORT void Dart_PropagateError(Dart_Handle handle) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| const Object& obj = Object::Handle(thread->zone(), Api::UnwrapHandle(handle)); |
| if (!obj.IsError()) { |
| FATAL1( |
| "%s expects argument 'handle' to be an error handle. " |
| "Did you forget to check Dart_IsError first?", |
| CURRENT_FUNC); |
| } |
| if (thread->top_exit_frame_info() == 0) { |
| // There are no dart frames on the stack so it would be illegal to |
| // propagate an error here. |
| FATAL("No Dart frames on stack, cannot propagate error."); |
| } |
| // Unwind all the API scopes till the exit frame before propagating. |
| const Error* error; |
| { |
| // We need to preserve the error object across the destruction of zones |
| // when the ApiScopes are unwound. By using NoSafepointScope, we can ensure |
| // that GC won't touch the raw error object before creating a valid |
| // handle for it in the surviving zone. |
| NoSafepointScope no_safepoint; |
| ErrorPtr raw_error = Api::UnwrapErrorHandle(thread->zone(), handle).ptr(); |
| thread->UnwindScopes(thread->top_exit_frame_info()); |
| // Note that thread's zone is different here than at the beginning of this |
| // function. |
| error = &Error::Handle(thread->zone(), raw_error); |
| } |
| Exceptions::PropagateError(*error); |
| UNREACHABLE(); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_ToString(Dart_Handle object) { |
| DARTSCOPE(Thread::Current()); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(object)); |
| if (obj.IsString()) { |
| return Api::NewHandle(T, obj.ptr()); |
| } else if (obj.IsInstance()) { |
| CHECK_CALLBACK_STATE(T); |
| const Instance& receiver = Instance::Cast(obj); |
| return Api::NewHandle(T, DartLibraryCalls::ToString(receiver)); |
| } else { |
| CHECK_CALLBACK_STATE(T); |
| // This is a VM internal object. Call the C++ method of printing. |
| return Api::NewHandle(T, String::New(obj.ToCString())); |
| } |
| } |
| |
| DART_EXPORT bool Dart_IdentityEquals(Dart_Handle obj1, Dart_Handle obj2) { |
| DARTSCOPE(Thread::Current()); |
| { |
| NoSafepointScope no_safepoint_scope; |
| if (Api::UnwrapHandle(obj1) == Api::UnwrapHandle(obj2)) { |
| return true; |
| } |
| } |
| const Object& object1 = Object::Handle(Z, Api::UnwrapHandle(obj1)); |
| const Object& object2 = Object::Handle(Z, Api::UnwrapHandle(obj2)); |
| if (object1.IsInstance() && object2.IsInstance()) { |
| return Instance::Cast(object1).IsIdenticalTo(Instance::Cast(object2)); |
| } |
| return false; |
| } |
| |
| DART_EXPORT Dart_Handle |
| Dart_HandleFromPersistent(Dart_PersistentHandle object) { |
| Thread* thread = Thread::Current(); |
| Isolate* isolate = thread->isolate(); |
| CHECK_ISOLATE(isolate); |
| ApiState* state = isolate->group()->api_state(); |
| ASSERT(state != NULL); |
| TransitionNativeToVM transition(thread); |
| NoSafepointScope no_safepoint_scope; |
| PersistentHandle* ref = PersistentHandle::Cast(object); |
| return Api::NewHandle(thread, ref->ptr()); |
| } |
| |
| DART_EXPORT Dart_Handle |
| Dart_HandleFromWeakPersistent(Dart_WeakPersistentHandle object) { |
| Thread* thread = Thread::Current(); |
| Isolate* isolate = thread->isolate(); |
| CHECK_ISOLATE(isolate); |
| ApiState* state = isolate->group()->api_state(); |
| ASSERT(state != NULL); |
| TransitionNativeToVM transition(thread); |
| NoSafepointScope no_safepoint_scope; |
| FinalizablePersistentHandle* weak_ref = |
| FinalizablePersistentHandle::Cast(object); |
| if (weak_ref->IsFinalizedNotFreed()) { |
| return Dart_Null(); |
| } |
| return Api::NewHandle(thread, weak_ref->ptr()); |
| } |
| |
| static Dart_Handle HandleFromFinalizable(Dart_FinalizableHandle object) { |
| Thread* thread = Thread::Current(); |
| Isolate* isolate = thread->isolate(); |
| CHECK_ISOLATE(isolate); |
| ApiState* state = isolate->group()->api_state(); |
| ASSERT(state != NULL); |
| TransitionNativeToVM transition(thread); |
| NoSafepointScope no_safepoint_scope; |
| FinalizablePersistentHandle* weak_ref = |
| FinalizablePersistentHandle::Cast(object); |
| return Api::NewHandle(thread, weak_ref->ptr()); |
| } |
| |
| DART_EXPORT Dart_PersistentHandle Dart_NewPersistentHandle(Dart_Handle object) { |
| DARTSCOPE(Thread::Current()); |
| Isolate* I = T->isolate(); |
| ApiState* state = I->group()->api_state(); |
| ASSERT(state != NULL); |
| const Object& old_ref = Object::Handle(Z, Api::UnwrapHandle(object)); |
| PersistentHandle* new_ref = state->AllocatePersistentHandle(); |
| new_ref->set_ptr(old_ref); |
| return new_ref->apiHandle(); |
| } |
| |
| DART_EXPORT void Dart_SetPersistentHandle(Dart_PersistentHandle obj1, |
| Dart_Handle obj2) { |
| DARTSCOPE(Thread::Current()); |
| Isolate* I = T->isolate(); |
| ApiState* state = I->group()->api_state(); |
| ASSERT(state != NULL); |
| ASSERT(state->IsValidPersistentHandle(obj1)); |
| const Object& obj2_ref = Object::Handle(Z, Api::UnwrapHandle(obj2)); |
| PersistentHandle* obj1_ref = PersistentHandle::Cast(obj1); |
| obj1_ref->set_ptr(obj2_ref); |
| } |
| |
| static bool IsFfiCompound(Thread* T, const Object& obj) { |
| if (obj.IsNull()) { |
| return false; |
| } |
| |
| // CFE guarantees we can only have direct subclasses of `Struct` and `Union` |
| // (no implementations or indirect subclasses are allowed). |
| const auto& klass = Class::Handle(Z, obj.clazz()); |
| const auto& super_klass = Class::Handle(Z, klass.SuperClass()); |
| if (super_klass.IsNull()) { |
| // This means klass is Object. |
| return false; |
| } |
| if (super_klass.Name() != Symbols::Struct().ptr() && |
| super_klass.Name() != Symbols::Union().ptr()) { |
| return false; |
| } |
| const auto& library = Library::Handle(Z, super_klass.library()); |
| return library.url() == Symbols::DartFfi().ptr(); |
| } |
| |
| static Dart_WeakPersistentHandle AllocateWeakPersistentHandle( |
| Thread* thread, |
| const Object& ref, |
| void* peer, |
| intptr_t external_allocation_size, |
| Dart_HandleFinalizer callback) { |
| if (!ref.ptr()->IsHeapObject()) { |
| return NULL; |
| } |
| if (ref.IsPointer()) { |
| return NULL; |
| } |
| if (IsFfiCompound(thread, ref)) { |
| return NULL; |
| } |
| |
| FinalizablePersistentHandle* finalizable_ref = |
| FinalizablePersistentHandle::New(thread->isolate_group(), ref, peer, |
| callback, external_allocation_size, |
| /*auto_delete=*/false); |
| return finalizable_ref->ApiWeakPersistentHandle(); |
| } |
| |
| static Dart_WeakPersistentHandle AllocateWeakPersistentHandle( |
| Thread* T, |
| Dart_Handle object, |
| void* peer, |
| intptr_t external_allocation_size, |
| Dart_HandleFinalizer callback) { |
| const auto& ref = Object::Handle(Z, Api::UnwrapHandle(object)); |
| return AllocateWeakPersistentHandle(T, ref, peer, external_allocation_size, |
| callback); |
| } |
| |
| static Dart_FinalizableHandle AllocateFinalizableHandle( |
| Thread* thread, |
| const Object& ref, |
| void* peer, |
| intptr_t external_allocation_size, |
| Dart_HandleFinalizer callback) { |
| if (!ref.ptr()->IsHeapObject()) { |
| return NULL; |
| } |
| if (ref.IsPointer()) { |
| return NULL; |
| } |
| if (IsFfiCompound(thread, ref)) { |
| return NULL; |
| } |
| |
| FinalizablePersistentHandle* finalizable_ref = |
| FinalizablePersistentHandle::New(thread->isolate_group(), ref, peer, |
| callback, external_allocation_size, |
| /*auto_delete=*/true); |
| return finalizable_ref->ApiFinalizableHandle(); |
| } |
| |
| static Dart_FinalizableHandle AllocateFinalizableHandle( |
| Thread* T, |
| Dart_Handle object, |
| void* peer, |
| intptr_t external_allocation_size, |
| Dart_HandleFinalizer callback) { |
| const auto& ref = Object::Handle(Z, Api::UnwrapHandle(object)); |
| return AllocateFinalizableHandle(T, ref, peer, external_allocation_size, |
| callback); |
| } |
| |
| DART_EXPORT Dart_WeakPersistentHandle |
| Dart_NewWeakPersistentHandle(Dart_Handle object, |
| void* peer, |
| intptr_t external_allocation_size, |
| Dart_HandleFinalizer callback) { |
| DARTSCOPE(Thread::Current()); |
| if (callback == NULL) { |
| return NULL; |
| } |
| |
| return AllocateWeakPersistentHandle(T, object, peer, external_allocation_size, |
| callback); |
| } |
| |
| DART_EXPORT Dart_FinalizableHandle |
| Dart_NewFinalizableHandle(Dart_Handle object, |
| void* peer, |
| intptr_t external_allocation_size, |
| Dart_HandleFinalizer callback) { |
| DARTSCOPE(Thread::Current()); |
| if (callback == nullptr) { |
| return nullptr; |
| } |
| |
| return AllocateFinalizableHandle(T, object, peer, external_allocation_size, |
| callback); |
| } |
| |
| DART_EXPORT void Dart_UpdateExternalSize(Dart_WeakPersistentHandle object, |
| intptr_t external_size) { |
| IsolateGroup* isolate_group = IsolateGroup::Current(); |
| CHECK_ISOLATE_GROUP(isolate_group); |
| NoSafepointScope no_safepoint_scope; |
| ApiState* state = isolate_group->api_state(); |
| ASSERT(state != NULL); |
| ASSERT(state->IsActiveWeakPersistentHandle(object)); |
| auto weak_ref = FinalizablePersistentHandle::Cast(object); |
| weak_ref->UpdateExternalSize(external_size, isolate_group); |
| } |
| |
| DART_EXPORT void Dart_UpdateFinalizableExternalSize( |
| Dart_FinalizableHandle object, |
| Dart_Handle strong_ref_to_object, |
| intptr_t external_allocation_size) { |
| if (!::Dart_IdentityEquals(strong_ref_to_object, |
| HandleFromFinalizable(object))) { |
| FATAL1( |
| "%s expects arguments 'object' and 'strong_ref_to_object' to point to " |
| "the same object.", |
| CURRENT_FUNC); |
| } |
| auto wph_object = reinterpret_cast<Dart_WeakPersistentHandle>(object); |
| ::Dart_UpdateExternalSize(wph_object, external_allocation_size); |
| } |
| |
| DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object) { |
| IsolateGroup* isolate_group = IsolateGroup::Current(); |
| CHECK_ISOLATE_GROUP(isolate_group); |
| NoSafepointScope no_safepoint_scope; |
| ApiState* state = isolate_group->api_state(); |
| ASSERT(state != NULL); |
| ASSERT(state->IsActivePersistentHandle(object)); |
| PersistentHandle* ref = PersistentHandle::Cast(object); |
| ASSERT(!state->IsProtectedHandle(ref)); |
| if (!state->IsProtectedHandle(ref)) { |
| state->FreePersistentHandle(ref); |
| } |
| } |
| |
| DART_EXPORT void Dart_DeleteWeakPersistentHandle( |
| Dart_WeakPersistentHandle object) { |
| IsolateGroup* isolate_group = IsolateGroup::Current(); |
| CHECK_ISOLATE_GROUP(isolate_group); |
| NoSafepointScope no_safepoint_scope; |
| ApiState* state = isolate_group->api_state(); |
| ASSERT(state != NULL); |
| ASSERT(state->IsActiveWeakPersistentHandle(object)); |
| auto weak_ref = FinalizablePersistentHandle::Cast(object); |
| weak_ref->EnsureFreedExternal(isolate_group); |
| state->FreeWeakPersistentHandle(weak_ref); |
| } |
| |
| DART_EXPORT void Dart_DeleteFinalizableHandle( |
| Dart_FinalizableHandle object, |
| Dart_Handle strong_ref_to_object) { |
| if (!::Dart_IdentityEquals(strong_ref_to_object, |
| HandleFromFinalizable(object))) { |
| FATAL1( |
| "%s expects arguments 'object' and 'strong_ref_to_object' to point to " |
| "the same object.", |
| CURRENT_FUNC); |
| } |
| |
| auto wph_object = reinterpret_cast<Dart_WeakPersistentHandle>(object); |
| |
| ::Dart_DeleteWeakPersistentHandle(wph_object); |
| } |
| |
| // --- Initialization and Globals --- |
| |
| DART_EXPORT const char* Dart_VersionString() { |
| return Version::String(); |
| } |
| |
| DART_EXPORT char* Dart_Initialize(Dart_InitializeParams* params) { |
| if (params == NULL) { |
| return Utils::StrDup( |
| "Dart_Initialize: " |
| "Dart_InitializeParams is null."); |
| } |
| |
| if (params->version != DART_INITIALIZE_PARAMS_CURRENT_VERSION) { |
| return Utils::StrDup( |
| "Dart_Initialize: " |
| "Invalid Dart_InitializeParams version."); |
| } |
| |
| return Dart::Init(params->vm_snapshot_data, params->vm_snapshot_instructions, |
| params->create_group, params->initialize_isolate, |
| params->shutdown_isolate, params->cleanup_isolate, |
| params->cleanup_group, params->thread_exit, |
| params->file_open, params->file_read, params->file_write, |
| params->file_close, params->entropy_source, |
| params->get_service_assets, params->start_kernel_isolate, |
| params->code_observer); |
| } |
| |
| DART_EXPORT char* Dart_Cleanup() { |
| CHECK_NO_ISOLATE(Isolate::Current()); |
| return Dart::Cleanup(); |
| } |
| |
| DART_EXPORT char* Dart_SetVMFlags(int argc, const char** argv) { |
| return Flags::ProcessCommandLineFlags(argc, argv); |
| } |
| |
| DART_EXPORT bool Dart_IsVMFlagSet(const char* flag_name) { |
| return Flags::IsSet(flag_name); |
| } |
| |
| #if !defined(PRODUCT) |
| #define VM_METRIC_API(type, variable, name, unit) \ |
| DART_EXPORT int64_t Dart_VM##variable##Metric() { \ |
| return vm_metric_##variable.Value(); \ |
| } |
| VM_METRIC_LIST(VM_METRIC_API); |
| #undef VM_METRIC_API |
| #else // !defined(PRODUCT) |
| #define VM_METRIC_API(type, variable, name, unit) \ |
| DART_EXPORT int64_t Dart_VM##variable##Metric() { return -1; } |
| VM_METRIC_LIST(VM_METRIC_API) |
| #undef VM_METRIC_API |
| #endif // !defined(PRODUCT) |
| |
| #define ISOLATE_GROUP_METRIC_API(type, variable, name, unit) \ |
| DART_EXPORT int64_t Dart_Isolate##variable##Metric(Dart_Isolate isolate) { \ |
| if (isolate == nullptr) { \ |
| FATAL1("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC); \ |
| } \ |
| Isolate* iso = reinterpret_cast<Isolate*>(isolate); \ |
| return iso->group()->Get##variable##Metric()->Value(); \ |
| } |
| ISOLATE_GROUP_METRIC_LIST(ISOLATE_GROUP_METRIC_API) |
| #undef ISOLATE_GROUP_METRIC_API |
| |
| #if !defined(PRODUCT) |
| #define ISOLATE_METRIC_API(type, variable, name, unit) \ |
| DART_EXPORT int64_t Dart_Isolate##variable##Metric(Dart_Isolate isolate) { \ |
| if (isolate == NULL) { \ |
| FATAL1("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC); \ |
| } \ |
| Isolate* iso = reinterpret_cast<Isolate*>(isolate); \ |
| return iso->Get##variable##Metric()->Value(); \ |
| } |
| ISOLATE_METRIC_LIST(ISOLATE_METRIC_API) |
| #undef ISOLATE_METRIC_API |
| #else // !defined(PRODUCT) |
| #define ISOLATE_METRIC_API(type, variable, name, unit) \ |
| DART_EXPORT int64_t Dart_Isolate##variable##Metric(Dart_Isolate isolate) { \ |
| return -1; \ |
| } |
| ISOLATE_METRIC_LIST(ISOLATE_METRIC_API) |
| #undef ISOLATE_METRIC_API |
| #endif // !defined(PRODUCT) |
| |
| // --- Isolates --- |
| |
| static Dart_Isolate CreateIsolate(IsolateGroup* group, |
| bool is_new_group, |
| const char* name, |
| void* isolate_data, |
| char** error) { |
| CHECK_NO_ISOLATE(Isolate::Current()); |
| |
| auto source = group->source(); |
| Isolate* I = Dart::CreateIsolate(name, source->flags, group); |
| if (I == NULL) { |
| if (error != NULL) { |
| *error = Utils::StrDup("Isolate creation failed"); |
| } |
| return reinterpret_cast<Dart_Isolate>(NULL); |
| } |
| |
| Thread* T = Thread::Current(); |
| bool success = false; |
| { |
| StackZone zone(T); |
| HANDLESCOPE(T); |
| // We enter an API scope here as InitializeIsolate could compile some |
| // bootstrap library files which call out to a tag handler that may create |
| // Api Handles when an error is encountered. |
| T->EnterApiScope(); |
| const Error& error_obj = Error::Handle( |
| Z, Dart::InitializeIsolate( |
| source->snapshot_data, source->snapshot_instructions, |
| source->kernel_buffer, source->kernel_buffer_size, |
| is_new_group ? nullptr : group, isolate_data)); |
| if (error_obj.IsNull()) { |
| #if defined(DEBUG) && !defined(DART_PRECOMPILED_RUNTIME) |
| if (FLAG_check_function_fingerprints && !FLAG_precompiled_mode) { |
| Library::CheckFunctionFingerprints(); |
| } |
| #endif // defined(DEBUG) && !defined(DART_PRECOMPILED_RUNTIME). |
| success = true; |
| } else if (error != NULL) { |
| *error = Utils::StrDup(error_obj.ToErrorCString()); |
| } |
| // We exit the API scope entered above. |
| T->ExitApiScope(); |
| } |
| |
| if (success) { |
| if (is_new_group) { |
| group->heap()->InitGrowthControl(); |
| } |
| // A Thread structure has been associated to the thread, we do the |
| // safepoint transition explicitly here instead of using the |
| // TransitionXXX scope objects as the reverse transition happens |
| // outside this scope in Dart_ShutdownIsolate/Dart_ExitIsolate. |
| T->set_execution_state(Thread::kThreadInNative); |
| T->EnterSafepoint(); |
| if (error != NULL) { |
| *error = NULL; |
| } |
| return Api::CastIsolate(I); |
| } |
| |
| Dart::ShutdownIsolate(); |
| return reinterpret_cast<Dart_Isolate>(NULL); |
| } |
| |
| static bool IsServiceOrKernelIsolateName(const char* name) { |
| if (ServiceIsolate::NameEquals(name)) { |
| ASSERT(!ServiceIsolate::Exists()); |
| return true; |
| } |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| if (KernelIsolate::NameEquals(name)) { |
| ASSERT(!KernelIsolate::Exists()); |
| return true; |
| } |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| return false; |
| } |
| |
| Isolate* CreateWithinExistingIsolateGroup(IsolateGroup* group, |
| const char* name, |
| char** error) { |
| API_TIMELINE_DURATION(Thread::Current()); |
| CHECK_NO_ISOLATE(Isolate::Current()); |
| |
| auto spawning_group = group; |
| |
| Isolate* isolate = reinterpret_cast<Isolate*>( |
| CreateIsolate(spawning_group, /*is_new_group=*/false, name, |
| /*isolate_data=*/nullptr, error)); |
| if (isolate == nullptr) return nullptr; |
| |
| auto source = spawning_group->source(); |
| ASSERT(isolate->source() == source); |
| |
| return isolate; |
| } |
| |
| DART_EXPORT void Dart_IsolateFlagsInitialize(Dart_IsolateFlags* flags) { |
| Isolate::FlagsInitialize(flags); |
| } |
| |
| DART_EXPORT Dart_Isolate |
| Dart_CreateIsolateGroup(const char* script_uri, |
| const char* name, |
| const uint8_t* snapshot_data, |
| const uint8_t* snapshot_instructions, |
| Dart_IsolateFlags* flags, |
| void* isolate_group_data, |
| void* isolate_data, |
| char** error) { |
| API_TIMELINE_DURATION(Thread::Current()); |
| |
| Dart_IsolateFlags api_flags; |
| if (flags == nullptr) { |
| Isolate::FlagsInitialize(&api_flags); |
| flags = &api_flags; |
| } |
| |
| const char* non_null_name = name == nullptr ? "isolate" : name; |
| std::unique_ptr<IsolateGroupSource> source( |
| new IsolateGroupSource(script_uri, non_null_name, snapshot_data, |
| snapshot_instructions, nullptr, -1, *flags)); |
| auto group = new IsolateGroup(std::move(source), isolate_group_data, *flags); |
| group->CreateHeap( |
| /*is_vm_isolate=*/false, IsServiceOrKernelIsolateName(non_null_name)); |
| IsolateGroup::RegisterIsolateGroup(group); |
| Dart_Isolate isolate = CreateIsolate(group, /*is_new_group=*/true, |
| non_null_name, isolate_data, error); |
| if (isolate != nullptr) { |
| group->set_initial_spawn_successful(); |
| } |
| return isolate; |
| } |
| |
| DART_EXPORT Dart_Isolate |
| Dart_CreateIsolateGroupFromKernel(const char* script_uri, |
| const char* name, |
| const uint8_t* kernel_buffer, |
| intptr_t kernel_buffer_size, |
| Dart_IsolateFlags* flags, |
| void* isolate_group_data, |
| void* isolate_data, |
| char** error) { |
| API_TIMELINE_DURATION(Thread::Current()); |
| |
| Dart_IsolateFlags api_flags; |
| if (flags == nullptr) { |
| Isolate::FlagsInitialize(&api_flags); |
| flags = &api_flags; |
| } |
| |
| const char* non_null_name = name == nullptr ? "isolate" : name; |
| std::shared_ptr<IsolateGroupSource> source( |
| new IsolateGroupSource(script_uri, non_null_name, nullptr, nullptr, |
| kernel_buffer, kernel_buffer_size, *flags)); |
| auto group = new IsolateGroup(source, isolate_group_data, *flags); |
| IsolateGroup::RegisterIsolateGroup(group); |
| group->CreateHeap( |
| /*is_vm_isolate=*/false, IsServiceOrKernelIsolateName(non_null_name)); |
| Dart_Isolate isolate = CreateIsolate(group, /*is_new_group=*/true, |
| non_null_name, isolate_data, error); |
| if (isolate != nullptr) { |
| group->set_initial_spawn_successful(); |
| } |
| return isolate; |
| } |
| |
| DART_EXPORT Dart_Isolate |
| Dart_CreateIsolateInGroup(Dart_Isolate group_member, |
| const char* name, |
| Dart_IsolateShutdownCallback shutdown_callback, |
| Dart_IsolateCleanupCallback cleanup_callback, |
| void* child_isolate_data, |
| char** error) { |
| CHECK_NO_ISOLATE(Isolate::Current()); |
| auto member = reinterpret_cast<Isolate*>(group_member); |
| if (member->IsScheduled()) { |
| FATAL("The given member isolate (%s) must not have been entered.", |
| member->name()); |
| } |
| |
| *error = nullptr; |
| |
| if (!IsolateGroup::AreIsolateGroupsEnabled()) { |
| *error = Utils::StrDup( |
| "Lightweight isolates are only implemented in AOT " |
| "mode and need to be explicitly enabled by passing " |
| "--enable-isolate-groups."); |
| return nullptr; |
| } |
| |
| Isolate* isolate; |
| #if defined(DART_PRECOMPILED_RUNTIME) |
| isolate = CreateWithinExistingIsolateGroup(member->group(), name, error); |
| if (isolate != nullptr) { |
| isolate->set_origin_id(member->origin_id()); |
| isolate->set_init_callback_data(child_isolate_data); |
| isolate->set_on_shutdown_callback(shutdown_callback); |
| isolate->set_on_cleanup_callback(cleanup_callback); |
| } |
| #else |
| *error = Utils::StrDup("Lightweight isolates are not yet ready in JIT mode."); |
| isolate = nullptr; |
| #endif |
| |
| return Api::CastIsolate(isolate); |
| } |
| |
| DART_EXPORT void Dart_ShutdownIsolate() { |
| Thread* T = Thread::Current(); |
| auto I = T->isolate(); |
| CHECK_ISOLATE(I); |
| |
| // The Thread structure is disassociated from the isolate, we do the |
| // safepoint transition explicitly here instead of using the TransitionXXX |
| // scope objects as the original transition happened outside this scope in |
| // Dart_EnterIsolate/Dart_CreateIsolateGroup. |
| ASSERT(T->execution_state() == Thread::kThreadInNative); |
| T->ExitSafepoint(); |
| T->set_execution_state(Thread::kThreadInVM); |
| |
| I->WaitForOutstandingSpawns(); |
| |
| // Release any remaining API scopes. |
| ApiLocalScope* scope = T->api_top_scope(); |
| while (scope != NULL) { |
| ApiLocalScope* previous = scope->previous(); |
| delete scope; |
| scope = previous; |
| } |
| T->set_api_top_scope(NULL); |
| |
| { |
| StackZone zone(T); |
| HandleScope handle_scope(T); |
| #if defined(DEBUG) |
| if (T->isolate()->origin_id() == 0) { |
| T->isolate_group()->ValidateConstants(); |
| } |
| #endif |
| Dart::RunShutdownCallback(); |
| } |
| Dart::ShutdownIsolate(); |
| } |
| |
| DART_EXPORT Dart_Isolate Dart_CurrentIsolate() { |
| return Api::CastIsolate(Isolate::Current()); |
| } |
| |
| DART_EXPORT void* Dart_CurrentIsolateData() { |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| NoSafepointScope no_safepoint_scope; |
| return isolate->init_callback_data(); |
| } |
| |
| DART_EXPORT void* Dart_IsolateData(Dart_Isolate isolate) { |
| if (isolate == NULL) { |
| FATAL1("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC); |
| } |
| // TODO(http://dartbug.com/16615): Validate isolate parameter. |
| return reinterpret_cast<Isolate*>(isolate)->init_callback_data(); |
| } |
| |
| DART_EXPORT Dart_IsolateGroup Dart_CurrentIsolateGroup() { |
| return Api::CastIsolateGroup(IsolateGroup::Current()); |
| } |
| |
| DART_EXPORT void* Dart_CurrentIsolateGroupData() { |
| IsolateGroup* isolate_group = IsolateGroup::Current(); |
| CHECK_ISOLATE_GROUP(isolate_group); |
| NoSafepointScope no_safepoint_scope; |
| return isolate_group->embedder_data(); |
| } |
| |
| DART_EXPORT void* Dart_IsolateGroupData(Dart_Isolate isolate) { |
| if (isolate == NULL) { |
| FATAL1("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC); |
| } |
| // TODO(http://dartbug.com/16615): Validate isolate parameter. |
| return reinterpret_cast<Isolate*>(isolate)->group()->embedder_data(); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_DebugName() { |
| DARTSCOPE(Thread::Current()); |
| Isolate* I = T->isolate(); |
| return Api::NewHandle( |
| T, String::NewFormatted("(%" Pd64 ") '%s'", |
| static_cast<int64_t>(I->main_port()), I->name())); |
| } |
| |
| DART_EXPORT const char* Dart_IsolateServiceId(Dart_Isolate isolate) { |
| if (isolate == NULL) { |
| FATAL1("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC); |
| } |
| // TODO(http://dartbug.com/16615): Validate isolate parameter. |
| Isolate* I = reinterpret_cast<Isolate*>(isolate); |
| int64_t main_port = static_cast<int64_t>(I->main_port()); |
| return OS::SCreate(NULL, "isolates/%" Pd64, main_port); |
| } |
| |
| DART_EXPORT void Dart_EnterIsolate(Dart_Isolate isolate) { |
| CHECK_NO_ISOLATE(Isolate::Current()); |
| // TODO(http://dartbug.com/16615): Validate isolate parameter. |
| Isolate* iso = reinterpret_cast<Isolate*>(isolate); |
| if (!Thread::EnterIsolate(iso)) { |
| if (iso->IsScheduled()) { |
| FATAL( |
| "Isolate %s is already scheduled on mutator thread %p, " |
| "failed to schedule from os thread 0x%" Px "\n", |
| iso->name(), iso->scheduled_mutator_thread(), |
| OSThread::ThreadIdToIntPtr(OSThread::GetCurrentThreadId())); |
| } else { |
| FATAL("Unable to enter isolate %s as Dart VM is shutting down", |
| iso->name()); |
| } |
| } |
| // A Thread structure has been associated to the thread, we do the |
| // safepoint transition explicitly here instead of using the |
| // TransitionXXX scope objects as the reverse transition happens |
| // outside this scope in Dart_ExitIsolate/Dart_ShutdownIsolate. |
| Thread* T = Thread::Current(); |
| T->set_execution_state(Thread::kThreadInNative); |
| T->EnterSafepoint(); |
| } |
| |
| DART_EXPORT void Dart_StartProfiling() { |
| #if !defined(PRODUCT) |
| if (!FLAG_profiler) { |
| FLAG_profiler = true; |
| Profiler::Init(); |
| } |
| #endif // !defined(PRODUCT) |
| } |
| |
| DART_EXPORT void Dart_StopProfiling() { |
| #if !defined(PRODUCT) |
| if (FLAG_profiler) { |
| Profiler::Cleanup(); |
| FLAG_profiler = false; |
| } |
| #endif // !defined(PRODUCT) |
| } |
| |
| DART_EXPORT void Dart_ThreadDisableProfiling() { |
| OSThread* os_thread = OSThread::Current(); |
| if (os_thread == NULL) { |
| return; |
| } |
| os_thread->DisableThreadInterrupts(); |
| } |
| |
| DART_EXPORT void Dart_ThreadEnableProfiling() { |
| OSThread* os_thread = OSThread::Current(); |
| if (os_thread == NULL) { |
| return; |
| } |
| os_thread->EnableThreadInterrupts(); |
| } |
| |
| DART_EXPORT void Dart_AddSymbols(const char* dso_name, |
| void* buffer, |
| intptr_t buffer_size) { |
| NativeSymbolResolver::AddSymbols(dso_name, buffer, buffer_size); |
| } |
| |
| DART_EXPORT bool Dart_WriteProfileToTimeline(Dart_Port main_port, |
| char** error) { |
| #if defined(PRODUCT) |
| return false; |
| #else |
| if (!FLAG_profiler) { |
| if (error != NULL) { |
| *error = Utils::StrDup("The profiler is not running."); |
| } |
| return false; |
| } |
| |
| const intptr_t kBufferLength = 512; |
| char method[kBufferLength]; |
| |
| // clang-format off |
| intptr_t method_length = snprintf(method, kBufferLength, "{" |
| "\"jsonrpc\": \"2.0\"," |
| "\"method\": \"_writeCpuProfileTimeline\"," |
| "\"id\": \"\"," |
| "\"params\": {" |
| " \"isolateId\": \"isolates/%" Pd64 "\"," |
| " \"tags\": \"None\"" |
| "}" |
| "}", main_port); |
| // clang-format on |
| ASSERT(method_length <= kBufferLength); |
| |
| char* response = NULL; |
| intptr_t response_length; |
| bool success = Dart_InvokeVMServiceMethod( |
| reinterpret_cast<uint8_t*>(method), method_length, |
| reinterpret_cast<uint8_t**>(&response), &response_length, error); |
| free(response); |
| return success; |
| #endif |
| } |
| |
| DART_EXPORT bool Dart_ShouldPauseOnStart() { |
| #if defined(PRODUCT) |
| return false; |
| #else |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| NoSafepointScope no_safepoint_scope; |
| return isolate->message_handler()->should_pause_on_start(); |
| #endif |
| } |
| |
| DART_EXPORT void Dart_SetShouldPauseOnStart(bool should_pause) { |
| #if defined(PRODUCT) |
| if (should_pause) { |
| FATAL1("%s(true) is not supported in a PRODUCT build", CURRENT_FUNC); |
| } |
| #else |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| NoSafepointScope no_safepoint_scope; |
| if (isolate->is_runnable()) { |
| FATAL1("%s expects the current isolate to not be runnable yet.", |
| CURRENT_FUNC); |
| } |
| isolate->message_handler()->set_should_pause_on_start(should_pause); |
| #endif |
| } |
| |
| DART_EXPORT bool Dart_IsPausedOnStart() { |
| #if defined(PRODUCT) |
| return false; |
| #else |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| NoSafepointScope no_safepoint_scope; |
| return isolate->message_handler()->is_paused_on_start(); |
| #endif |
| } |
| |
| DART_EXPORT void Dart_SetPausedOnStart(bool paused) { |
| #if defined(PRODUCT) |
| if (paused) { |
| FATAL1("%s(true) is not supported in a PRODUCT build", CURRENT_FUNC); |
| } |
| #else |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| NoSafepointScope no_safepoint_scope; |
| if (isolate->message_handler()->is_paused_on_start() != paused) { |
| isolate->message_handler()->PausedOnStart(paused); |
| } |
| #endif |
| } |
| |
| DART_EXPORT bool Dart_ShouldPauseOnExit() { |
| #if defined(PRODUCT) |
| return false; |
| #else |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| NoSafepointScope no_safepoint_scope; |
| return isolate->message_handler()->should_pause_on_exit(); |
| #endif |
| } |
| |
| DART_EXPORT void Dart_SetShouldPauseOnExit(bool should_pause) { |
| #if defined(PRODUCT) |
| if (should_pause) { |
| FATAL1("%s(true) is not supported in a PRODUCT build", CURRENT_FUNC); |
| } |
| #else |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| NoSafepointScope no_safepoint_scope; |
| isolate->message_handler()->set_should_pause_on_exit(should_pause); |
| #endif |
| } |
| |
| DART_EXPORT bool Dart_IsPausedOnExit() { |
| #if defined(PRODUCT) |
| return false; |
| #else |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| NoSafepointScope no_safepoint_scope; |
| return isolate->message_handler()->is_paused_on_exit(); |
| #endif |
| } |
| |
| DART_EXPORT void Dart_SetPausedOnExit(bool paused) { |
| #if defined(PRODUCT) |
| if (paused) { |
| FATAL1("%s(true) is not supported in a PRODUCT build", CURRENT_FUNC); |
| } |
| #else |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| NoSafepointScope no_safepoint_scope; |
| if (isolate->message_handler()->is_paused_on_exit() != paused) { |
| isolate->message_handler()->PausedOnExit(paused); |
| } |
| #endif |
| } |
| |
| DART_EXPORT void Dart_SetStickyError(Dart_Handle error) { |
| Thread* thread = Thread::Current(); |
| DARTSCOPE(thread); |
| Isolate* isolate = thread->isolate(); |
| CHECK_ISOLATE(isolate); |
| NoSafepointScope no_safepoint_scope; |
| const Error& error_handle = Api::UnwrapErrorHandle(Z, error); |
| if ((isolate->sticky_error() != Error::null()) && |
| (error_handle.ptr() != Object::null())) { |
| FATAL1("%s expects there to be no sticky error.", CURRENT_FUNC); |
| } |
| if (!error_handle.IsUnhandledException() && |
| (error_handle.ptr() != Object::null())) { |
| FATAL1("%s expects the error to be an unhandled exception error or null.", |
| CURRENT_FUNC); |
| } |
| isolate->SetStickyError(error_handle.ptr()); |
| } |
| |
| DART_EXPORT bool Dart_HasStickyError() { |
| Thread* T = Thread::Current(); |
| Isolate* isolate = T->isolate(); |
| CHECK_ISOLATE(isolate); |
| NoSafepointScope no_safepoint_scope; |
| return isolate->sticky_error() != Error::null(); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_GetStickyError() { |
| Thread* T = Thread::Current(); |
| Isolate* I = T->isolate(); |
| CHECK_ISOLATE(I); |
| { |
| NoSafepointScope no_safepoint_scope; |
| if (I->sticky_error() == Error::null()) { |
| return Api::Null(); |
| } |
| } |
| TransitionNativeToVM transition(T); |
| return Api::NewHandle(T, I->sticky_error()); |
| } |
| |
| DART_EXPORT void Dart_HintFreed(intptr_t size) { |
| if (size < 0) { |
| FATAL1("%s requires a non-negative size", CURRENT_FUNC); |
| } |
| Thread* T = Thread::Current(); |
| CHECK_ISOLATE(T->isolate()); |
| API_TIMELINE_BEGIN_END(T); |
| TransitionNativeToVM transition(T); |
| T->heap()->HintFreed(size); |
| } |
| |
| DART_EXPORT void Dart_NotifyIdle(int64_t deadline) { |
| Thread* T = Thread::Current(); |
| CHECK_ISOLATE(T->isolate()); |
| API_TIMELINE_BEGIN_END(T); |
| TransitionNativeToVM transition(T); |
| T->isolate()->group()->idle_time_handler()->NotifyIdle(deadline); |
| } |
| |
| DART_EXPORT void Dart_NotifyLowMemory() { |
| API_TIMELINE_BEGIN_END(Thread::Current()); |
| Isolate::NotifyLowMemory(); |
| } |
| |
| DART_EXPORT void Dart_ExitIsolate() { |
| Thread* T = Thread::Current(); |
| CHECK_ISOLATE(T->isolate()); |
| // The Thread structure is disassociated from the isolate, we do the |
| // safepoint transition explicitly here instead of using the TransitionXXX |
| // scope objects as the original transition happened outside this scope in |
| // Dart_EnterIsolate/Dart_CreateIsolateGroup. |
| ASSERT(T->execution_state() == Thread::kThreadInNative); |
| T->ExitSafepoint(); |
| T->set_execution_state(Thread::kThreadInVM); |
| Thread::ExitIsolate(); |
| } |
| |
| DART_EXPORT Dart_Handle |
| Dart_CreateSnapshot(uint8_t** vm_snapshot_data_buffer, |
| intptr_t* vm_snapshot_data_size, |
| uint8_t** isolate_snapshot_data_buffer, |
| intptr_t* isolate_snapshot_data_size, |
| bool is_core) { |
| #if defined(DART_PRECOMPILED_RUNTIME) |
| return Api::NewError("Cannot create snapshots on an AOT runtime."); |
| #else |
| DARTSCOPE(Thread::Current()); |
| API_TIMELINE_DURATION(T); |
| if (vm_snapshot_data_buffer != nullptr) { |
| CHECK_NULL(vm_snapshot_data_size); |
| } |
| CHECK_NULL(isolate_snapshot_data_buffer); |
| CHECK_NULL(isolate_snapshot_data_size); |
| // Finalize all classes if needed. |
| Dart_Handle state = Api::CheckAndFinalizePendingClasses(T); |
| if (Api::IsError(state)) { |
| return state; |
| } |
| NoBackgroundCompilerScope no_bg_compiler(T); |
| |
| #if defined(DEBUG) |
| T->isolate_group()->heap()->CollectAllGarbage(); |
| { |
| HeapIterationScope iteration(T); |
| CheckFunctionTypesVisitor check_canonical(T); |
| iteration.IterateObjects(&check_canonical); |
| } |
| #endif // #if defined(DEBUG) |
| |
| ZoneWriteStream vm_snapshot_data(Api::TopScope(T)->zone(), |
| FullSnapshotWriter::kInitialSize); |
| ZoneWriteStream isolate_snapshot_data(Api::TopScope(T)->zone(), |
| FullSnapshotWriter::kInitialSize); |
| const Snapshot::Kind snapshot_kind = |
| is_core ? Snapshot::kFullCore : Snapshot::kFull; |
| FullSnapshotWriter writer( |
| snapshot_kind, &vm_snapshot_data, &isolate_snapshot_data, |
| nullptr /* vm_image_writer */, nullptr /* isolate_image_writer */); |
| writer.WriteFullSnapshot(); |
| if (vm_snapshot_data_buffer != nullptr) { |
| *vm_snapshot_data_buffer = vm_snapshot_data.buffer(); |
| *vm_snapshot_data_size = writer.VmIsolateSnapshotSize(); |
| } |
| *isolate_snapshot_data_buffer = isolate_snapshot_data.buffer(); |
| *isolate_snapshot_data_size = writer.IsolateSnapshotSize(); |
| return Api::Success(); |
| #endif |
| } |
| |
| DART_EXPORT bool Dart_IsKernel(const uint8_t* buffer, intptr_t buffer_size) { |
| if (buffer_size < 4) { |
| return false; |
| } |
| return (buffer[0] == 0x90) && (buffer[1] == 0xab) && (buffer[2] == 0xcd) && |
| (buffer[3] == 0xef); |
| } |
| |
| DART_EXPORT char* Dart_IsolateMakeRunnable(Dart_Isolate isolate) { |
| CHECK_NO_ISOLATE(Isolate::Current()); |
| API_TIMELINE_DURATION(Thread::Current()); |
| if (isolate == NULL) { |
| FATAL1("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC); |
| } |
| // TODO(16615): Validate isolate parameter. |
| const char* error = reinterpret_cast<Isolate*>(isolate)->MakeRunnable(); |
| if (error != nullptr) { |
| return Utils::StrDup(error); |
| } |
| return nullptr; |
| } |
| |
| // --- Messages and Ports --- |
| |
| DART_EXPORT void Dart_SetMessageNotifyCallback( |
| Dart_MessageNotifyCallback message_notify_callback) { |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| |
| { |
| NoSafepointScope no_safepoint_scope; |
| isolate->set_message_notify_callback(message_notify_callback); |
| } |
| |
| if (message_notify_callback != nullptr && isolate->HasPendingMessages()) { |
| ::Dart_ExitIsolate(); |
| |
| // If a new handler gets installed and there are pending messages in the |
| // queue (e.g. OOB messages for doing vm service work) we need to notify |
| // the newly registered callback, otherwise the embedder might never get |
| // notified about the pending messages. |
| message_notify_callback(Api::CastIsolate(isolate)); |
| |
| ::Dart_EnterIsolate(Api::CastIsolate(isolate)); |
| } |
| } |
| |
| DART_EXPORT Dart_MessageNotifyCallback Dart_GetMessageNotifyCallback() { |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| NoSafepointScope no_safepoint_scope; |
| return isolate->message_notify_callback(); |
| } |
| |
| struct RunLoopData { |
| Monitor* monitor; |
| bool done; |
| }; |
| |
| static void RunLoopDone(uword param) { |
| RunLoopData* data = reinterpret_cast<RunLoopData*>(param); |
| ASSERT(data->monitor != NULL); |
| MonitorLocker ml(data->monitor); |
| data->done = true; |
| ml.Notify(); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_RunLoop() { |
| Isolate* I; |
| { |
| Thread* T = Thread::Current(); |
| I = T->isolate(); |
| CHECK_API_SCOPE(T); |
| CHECK_CALLBACK_STATE(T); |
| } |
| API_TIMELINE_BEGIN_END(Thread::Current()); |
| // The message handler run loop does not expect to have a current isolate |
| // so we exit the isolate here and enter it again after the runloop is done. |
| ::Dart_ExitIsolate(); |
| { |
| Monitor monitor; |
| MonitorLocker ml(&monitor); |
| RunLoopData data; |
| data.monitor = &monitor; |
| data.done = false; |
| I->message_handler()->Run(I->group()->thread_pool(), NULL, RunLoopDone, |
| reinterpret_cast<uword>(&data)); |
| while (!data.done) { |
| ml.Wait(); |
| } |
| } |
| ::Dart_EnterIsolate(Api::CastIsolate(I)); |
| if (I->sticky_error() != Object::null()) { |
| Thread* T = Thread::Current(); |
| TransitionNativeToVM transition(T); |
| return Api::NewHandle(T, I->StealStickyError()); |
| } |
| if (FLAG_print_class_table) { |
| HANDLESCOPE(Thread::Current()); |
| I->group()->class_table()->Print(); |
| } |
| return Api::Success(); |
| } |
| |
| DART_EXPORT bool Dart_RunLoopAsync(bool errors_are_fatal, |
| Dart_Port on_error_port, |
| Dart_Port on_exit_port, |
| char** error) { |
| auto thread = Thread::Current(); |
| auto isolate = thread->isolate(); |
| CHECK_ISOLATE(isolate); |
| *error = nullptr; |
| |
| if (thread->api_top_scope() != nullptr) { |
| *error = Utils::StrDup("There must not be an active api scope."); |
| return false; |
| } |
| |
| if (!isolate->is_runnable()) { |
| const char* error_msg = isolate->MakeRunnable(); |
| if (error_msg != nullptr) { |
| *error = Utils::StrDup(error_msg); |
| return false; |
| } |
| } |
| |
| isolate->SetErrorsFatal(errors_are_fatal); |
| |
| if (on_error_port != ILLEGAL_PORT || on_exit_port != ILLEGAL_PORT) { |
| auto thread = Thread::Current(); |
| TransitionNativeToVM transition(thread); |
| StackZone zone(thread); |
| HANDLESCOPE(thread); |
| |
| if (on_error_port != ILLEGAL_PORT) { |
| const auto& port = |
| SendPort::Handle(thread->zone(), SendPort::New(on_error_port)); |
| isolate->AddErrorListener(port); |
| } |
| if (on_exit_port != ILLEGAL_PORT) { |
| const auto& port = |
| SendPort::Handle(thread->zone(), SendPort::New(on_exit_port)); |
| isolate->AddExitListener(port, Instance::null_instance()); |
| } |
| } |
| |
| Dart_ExitIsolate(); |
| isolate->Run(); |
| return true; |
| } |
| |
| DART_EXPORT Dart_Handle Dart_HandleMessage() { |
| Thread* T = Thread::Current(); |
| Isolate* I = T->isolate(); |
| CHECK_API_SCOPE(T); |
| CHECK_CALLBACK_STATE(T); |
| API_TIMELINE_BEGIN_END_BASIC(T); |
| TransitionNativeToVM transition(T); |
| if (I->message_handler()->HandleNextMessage() != MessageHandler::kOK) { |
| return Api::NewHandle(T, T->StealStickyError()); |
| } |
| return Api::Success(); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_WaitForEvent(int64_t timeout_millis) { |
| Thread* T = Thread::Current(); |
| Isolate* I = T->isolate(); |
| CHECK_API_SCOPE(T); |
| CHECK_CALLBACK_STATE(T); |
| API_TIMELINE_BEGIN_END_BASIC(T); |
| TransitionNativeToVM transition(T); |
| if (I->message_notify_callback() != NULL) { |
| return Api::NewError("waitForEventSync is not supported by this embedder"); |
| } |
| Object& result = |
| Object::Handle(Z, DartLibraryCalls::EnsureScheduleImmediate()); |
| if (result.IsError()) { |
| return Api::NewHandle(T, result.ptr()); |
| } |
| |
| // Drain the microtask queue. Propagate any errors to the entry frame. |
| result = DartLibraryCalls::DrainMicrotaskQueue(); |
| if (result.IsError()) { |
| // Persist the error across unwiding scopes before propagating. |
| const Error* error; |
| { |
| NoSafepointScope no_safepoint; |
| ErrorPtr raw_error = Error::Cast(result).ptr(); |
| T->UnwindScopes(T->top_exit_frame_info()); |
| error = &Error::Handle(T->zone(), raw_error); |
| } |
| Exceptions::PropagateToEntry(*error); |
| UNREACHABLE(); |
| return Api::NewError("Unreachable"); |
| } |
| |
| // Block to wait for messages and then handle them. Propagate any errors to |
| // the entry frame. |
| if (I->message_handler()->PauseAndHandleAllMessages(timeout_millis) != |
| MessageHandler::kOK) { |
| // Persist the error across unwiding scopes before propagating. |
| const Error* error; |
| { |
| NoSafepointScope no_safepoint; |
| ErrorPtr raw_error = T->StealStickyError(); |
| T->UnwindScopes(T->top_exit_frame_info()); |
| error = &Error::Handle(T->zone(), raw_error); |
| } |
| Exceptions::PropagateToEntry(*error); |
| UNREACHABLE(); |
| return Api::NewError("Unreachable"); |
| } |
| return Api::Success(); |
| } |
| |
| DART_EXPORT bool Dart_HandleServiceMessages() { |
| #if defined(PRODUCT) |
| return true; |
| #else |
| Thread* T = Thread::Current(); |
| Isolate* I = T->isolate(); |
| CHECK_API_SCOPE(T); |
| CHECK_CALLBACK_STATE(T); |
| API_TIMELINE_DURATION(T); |
| TransitionNativeToVM transition(T); |
| ASSERT(I->GetAndClearResumeRequest() == false); |
| MessageHandler::MessageStatus status = |
| I->message_handler()->HandleOOBMessages(); |
| bool resume = I->GetAndClearResumeRequest(); |
| return (status != MessageHandler::kOK) || resume; |
| #endif |
| } |
| |
| DART_EXPORT bool Dart_HasServiceMessages() { |
| #if defined(PRODUCT) |
| return false; |
| #else |
| Isolate* isolate = Isolate::Current(); |
| ASSERT(isolate); |
| NoSafepointScope no_safepoint_scope; |
| return isolate->message_handler()->HasOOBMessages(); |
| #endif |
| } |
| |
| DART_EXPORT bool Dart_HasLivePorts() { |
| Isolate* isolate = Isolate::Current(); |
| ASSERT(isolate); |
| NoSafepointScope no_safepoint_scope; |
| return isolate->message_handler()->HasLivePorts(); |
| } |
| |
| DART_EXPORT bool Dart_Post(Dart_Port port_id, Dart_Handle handle) { |
| DARTSCOPE(Thread::Current()); |
| API_TIMELINE_DURATION(T); |
| NoSafepointScope no_safepoint_scope; |
| if (port_id == ILLEGAL_PORT) { |
| return false; |
| } |
| |
| // Smis and null can be sent without serialization. |
| ObjectPtr raw_obj = Api::UnwrapHandle(handle); |
| if (ApiObjectConverter::CanConvert(raw_obj)) { |
| return PortMap::PostMessage( |
| Message::New(port_id, raw_obj, Message::kNormalPriority)); |
| } |
| |
| const Object& object = Object::Handle(Z, raw_obj); |
| MessageWriter writer(false); |
| return PortMap::PostMessage( |
| writer.WriteMessage(object, port_id, Message::kNormalPriority)); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_NewSendPort(Dart_Port port_id) { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| if (port_id == ILLEGAL_PORT) { |
| return Api::NewError("%s: illegal port_id %" Pd64 ".", CURRENT_FUNC, |
| port_id); |
| } |
| return Api::NewHandle(T, SendPort::New(port_id)); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_SendPortGetId(Dart_Handle port, |
| Dart_Port* port_id) { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| API_TIMELINE_DURATION(T); |
| const SendPort& send_port = Api::UnwrapSendPortHandle(Z, port); |
| if (send_port.IsNull()) { |
| RETURN_TYPE_ERROR(Z, port, SendPort); |
| } |
| if (port_id == NULL) { |
| RETURN_NULL_ERROR(port_id); |
| } |
| *port_id = send_port.Id(); |
| return Api::Success(); |
| } |
| |
| DART_EXPORT Dart_Port Dart_GetMainPortId() { |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| return isolate->main_port(); |
| } |
| |
| // --- Scopes ---- |
| |
| DART_EXPORT void Dart_EnterScope() { |
| Thread* thread = Thread::Current(); |
| Isolate* isolate = thread->isolate(); |
| CHECK_ISOLATE(isolate); |
| TransitionNativeToVM transition(thread); |
| thread->EnterApiScope(); |
| } |
| |
| DART_EXPORT void Dart_ExitScope() { |
| Thread* thread = Thread::Current(); |
| CHECK_API_SCOPE(thread); |
| TransitionNativeToVM transition(thread); |
| thread->ExitApiScope(); |
| } |
| |
| DART_EXPORT uint8_t* Dart_ScopeAllocate(intptr_t size) { |
| Zone* zone; |
| Thread* thread = Thread::Current(); |
| if (thread != NULL) { |
| ApiLocalScope* scope = thread->api_top_scope(); |
| zone = scope->zone(); |
| } else { |
| ApiNativeScope* scope = ApiNativeScope::Current(); |
| if (scope == NULL) return NULL; |
| zone = scope->zone(); |
| } |
| return reinterpret_cast<uint8_t*>(zone->AllocUnsafe(size)); |
| } |
| |
| // --- Objects ---- |
| |
| DART_EXPORT Dart_Handle Dart_Null() { |
| ASSERT(Isolate::Current() != NULL); |
| return Api::Null(); |
| } |
| |
| DART_EXPORT bool Dart_IsNull(Dart_Handle object) { |
| TransitionNativeToVM transition(Thread::Current()); |
| return Api::UnwrapHandle(object) == Object::null(); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_EmptyString() { |
| ASSERT(Isolate::Current() != NULL); |
| return Api::EmptyString(); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_TypeDynamic() { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| API_TIMELINE_DURATION(T); |
| return Api::NewHandle(T, Type::DynamicType()); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_TypeVoid() { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| API_TIMELINE_DURATION(T); |
| return Api::NewHandle(T, Type::VoidType()); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_TypeNever() { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| API_TIMELINE_DURATION(T); |
| return Api::NewHandle(T, Type::NeverType()); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_ObjectEquals(Dart_Handle obj1, |
| Dart_Handle obj2, |
| bool* value) { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| const Instance& expected = |
| Instance::CheckedHandle(Z, Api::UnwrapHandle(obj1)); |
| const Instance& actual = Instance::CheckedHandle(Z, Api::UnwrapHandle(obj2)); |
| const Object& result = |
| Object::Handle(Z, DartLibraryCalls::Equals(expected, actual)); |
| if (result.IsBool()) { |
| *value = Bool::Cast(result).value(); |
| return Api::Success(); |
| } else if (result.IsError()) { |
| return Api::NewHandle(T, result.ptr()); |
| } else { |
| return Api::NewError("Expected boolean result from =="); |
| } |
| } |
| |
| // Assumes type is non-null. |
| static bool InstanceIsType(const Thread* thread, |
| const Instance& instance, |
| const Type& type) { |
| ASSERT(!type.IsNull()); |
| CHECK_CALLBACK_STATE(thread); |
| return instance.IsInstanceOf(type, Object::null_type_arguments(), |
| Object::null_type_arguments()); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_ObjectIsType(Dart_Handle object, |
| Dart_Handle type, |
| bool* value) { |
| DARTSCOPE(Thread::Current()); |
| |
| const Type& type_obj = Api::UnwrapTypeHandle(Z, type); |
| if (type_obj.IsNull()) { |
| *value = false; |
| RETURN_TYPE_ERROR(Z, type, Type); |
| } |
| if (!type_obj.IsFinalized()) { |
| return Api::NewError( |
| "%s expects argument 'type' to be a fully resolved type.", |
| CURRENT_FUNC); |
| } |
| if (object == Api::Null()) { |
| *value = false; |
| return Api::Success(); |
| } |
| const Instance& instance = Api::UnwrapInstanceHandle(Z, object); |
| if (instance.IsNull()) { |
| *value = false; |
| RETURN_TYPE_ERROR(Z, object, Instance); |
| } |
| *value = InstanceIsType(T, instance, type_obj); |
| return Api::Success(); |
| } |
| |
| DART_EXPORT bool Dart_IsInstance(Dart_Handle object) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| REUSABLE_OBJECT_HANDLESCOPE(thread); |
| Object& ref = thread->ObjectHandle(); |
| ref = Api::UnwrapHandle(object); |
| return ref.IsInstance(); |
| } |
| |
| DART_EXPORT bool Dart_IsNumber(Dart_Handle object) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| return IsNumberClassId(Api::ClassId(object)); |
| } |
| |
| DART_EXPORT bool Dart_IsInteger(Dart_Handle object) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| return IsIntegerClassId(Api::ClassId(object)); |
| } |
| |
| DART_EXPORT bool Dart_IsDouble(Dart_Handle object) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| return Api::ClassId(object) == kDoubleCid; |
| } |
| |
| DART_EXPORT bool Dart_IsBoolean(Dart_Handle object) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| return Api::ClassId(object) == kBoolCid; |
| } |
| |
| DART_EXPORT bool Dart_IsString(Dart_Handle object) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| return IsStringClassId(Api::ClassId(object)); |
| } |
| |
| DART_EXPORT bool Dart_IsStringLatin1(Dart_Handle object) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| return IsOneByteStringClassId(Api::ClassId(object)); |
| } |
| |
| DART_EXPORT bool Dart_IsExternalString(Dart_Handle object) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| return IsExternalStringClassId(Api::ClassId(object)); |
| } |
| |
| DART_EXPORT bool Dart_IsList(Dart_Handle object) { |
| DARTSCOPE(Thread::Current()); |
| if (IsBuiltinListClassId(Api::ClassId(object))) { |
| return true; |
| } |
| |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(object)); |
| return GetListInstance(Z, obj) != Instance::null(); |
| } |
| |
| DART_EXPORT bool Dart_IsMap(Dart_Handle object) { |
| DARTSCOPE(Thread::Current()); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(object)); |
| return GetMapInstance(Z, obj) != Instance::null(); |
| } |
| |
| DART_EXPORT bool Dart_IsLibrary(Dart_Handle object) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| return Api::ClassId(object) == kLibraryCid; |
| } |
| |
| DART_EXPORT bool Dart_IsType(Dart_Handle handle) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| return IsTypeClassId(Api::ClassId(handle)); |
| } |
| |
| DART_EXPORT bool Dart_IsFunction(Dart_Handle handle) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| return Api::ClassId(handle) == kFunctionCid; |
| } |
| |
| DART_EXPORT bool Dart_IsVariable(Dart_Handle handle) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| return Api::ClassId(handle) == kFieldCid; |
| } |
| |
| DART_EXPORT bool Dart_IsTypeVariable(Dart_Handle handle) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| return Api::ClassId(handle) == kTypeParameterCid; |
| } |
| |
| DART_EXPORT bool Dart_IsClosure(Dart_Handle object) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| return Api::ClassId(object) == kClosureCid; |
| } |
| |
| DART_EXPORT bool Dart_IsTearOff(Dart_Handle object) { |
| DARTSCOPE(Thread::Current()); |
| API_TIMELINE_DURATION(T); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(object)); |
| if (obj.IsClosure()) { |
| const Closure& closure = Closure::Cast(obj); |
| const Function& func = Function::Handle(Z, closure.function()); |
| return func.IsImplicitClosureFunction(); |
| } |
| return false; |
| } |
| |
| DART_EXPORT bool Dart_IsTypedData(Dart_Handle handle) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| intptr_t cid = Api::ClassId(handle); |
| return IsTypedDataClassId(cid) || IsExternalTypedDataClassId(cid) || |
| IsTypedDataViewClassId(cid); |
| } |
| |
| DART_EXPORT bool Dart_IsByteBuffer(Dart_Handle handle) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| TransitionNativeToVM transition(thread); |
| return Api::ClassId(handle) == kByteBufferCid; |
| } |
| |
| DART_EXPORT bool Dart_IsFuture(Dart_Handle handle) { |
| DARTSCOPE(Thread::Current()); |
| API_TIMELINE_DURATION(T); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(handle)); |
| if (obj.IsInstance()) { |
| ObjectStore* object_store = T->isolate_group()->object_store(); |
| const Type& future_rare_type = |
| Type::Handle(Z, object_store->non_nullable_future_rare_type()); |
| ASSERT(!future_rare_type.IsNull()); |
| const Class& obj_class = Class::Handle(Z, obj.clazz()); |
| bool is_future = Class::IsSubtypeOf( |
| obj_class, Object::null_type_arguments(), Nullability::kNonNullable, |
| future_rare_type, Heap::kNew); |
| return is_future; |
| } |
| return false; |
| } |
| |
| // --- Instances ---- |
| |
| DART_EXPORT Dart_Handle Dart_InstanceGetType(Dart_Handle instance) { |
| DARTSCOPE(Thread::Current()); |
| API_TIMELINE_DURATION(T); |
| auto isolate_group = T->isolate_group(); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(instance)); |
| if (obj.IsNull()) { |
| return Api::NewHandle(T, isolate_group->object_store()->null_type()); |
| } |
| if (!obj.IsInstance()) { |
| RETURN_TYPE_ERROR(Z, instance, Instance); |
| } |
| const AbstractType& type = |
| AbstractType::Handle(Instance::Cast(obj).GetType(Heap::kNew)); |
| return Api::NewHandle(T, type.Canonicalize(T, nullptr)); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_FunctionName(Dart_Handle function) { |
| DARTSCOPE(Thread::Current()); |
| const Function& func = Api::UnwrapFunctionHandle(Z, function); |
| if (func.IsNull()) { |
| RETURN_TYPE_ERROR(Z, function, Function); |
| } |
| return Api::NewHandle(T, func.UserVisibleName()); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_ClassName(Dart_Handle cls_type) { |
| DARTSCOPE(Thread::Current()); |
| const Type& type_obj = Api::UnwrapTypeHandle(Z, cls_type); |
| if (type_obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, cls_type, Type); |
| } |
| const Class& klass = Class::Handle(Z, type_obj.type_class()); |
| if (klass.IsNull()) { |
| return Api::NewError( |
| "cls_type must be a Type object which represents a Class."); |
| } |
| return Api::NewHandle(T, klass.UserVisibleName()); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_FunctionOwner(Dart_Handle function) { |
| DARTSCOPE(Thread::Current()); |
| const Function& func = Api::UnwrapFunctionHandle(Z, function); |
| if (func.IsNull()) { |
| RETURN_TYPE_ERROR(Z, function, Function); |
| } |
| if (func.IsNonImplicitClosureFunction()) { |
| FunctionPtr parent_function = func.parent_function(); |
| return Api::NewHandle(T, parent_function); |
| } |
| const Class& owner = Class::Handle(Z, func.Owner()); |
| ASSERT(!owner.IsNull()); |
| if (owner.IsTopLevel()) { |
| // Top-level functions are implemented as members of a hidden class. We hide |
| // that class here and instead answer the library. |
| #if defined(DEBUG) |
| const Library& lib = Library::Handle(Z, owner.library()); |
| if (lib.IsNull()) { |
| ASSERT(owner.IsDynamicClass() || owner.IsVoidClass() || |
| owner.IsNeverClass()); |
| } |
| #endif |
| return Api::NewHandle(T, owner.library()); |
| } else { |
| return Api::NewHandle(T, owner.RareType()); |
| } |
| } |
| |
| DART_EXPORT Dart_Handle Dart_FunctionIsStatic(Dart_Handle function, |
| bool* is_static) { |
| DARTSCOPE(Thread::Current()); |
| if (is_static == NULL) { |
| RETURN_NULL_ERROR(is_static); |
| } |
| const Function& func = Api::UnwrapFunctionHandle(Z, function); |
| if (func.IsNull()) { |
| RETURN_TYPE_ERROR(Z, function, Function); |
| } |
| *is_static = func.is_static(); |
| return Api::Success(); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_ClosureFunction(Dart_Handle closure) { |
| DARTSCOPE(Thread::Current()); |
| const Instance& closure_obj = Api::UnwrapInstanceHandle(Z, closure); |
| if (closure_obj.IsNull() || !closure_obj.IsClosure()) { |
| RETURN_TYPE_ERROR(Z, closure, Instance); |
| } |
| |
| ASSERT(ClassFinalizer::AllClassesFinalized()); |
| |
| FunctionPtr rf = Closure::Cast(closure_obj).function(); |
| return Api::NewHandle(T, rf); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_ClassLibrary(Dart_Handle cls_type) { |
| DARTSCOPE(Thread::Current()); |
| const Type& type_obj = Api::UnwrapTypeHandle(Z, cls_type); |
| const Class& klass = Class::Handle(Z, type_obj.type_class()); |
| if (klass.IsNull()) { |
| return Api::NewError( |
| "cls_type must be a Type object which represents a Class."); |
| } |
| const Library& library = Library::Handle(klass.library()); |
| if (library.IsNull()) { |
| return Dart_Null(); |
| } |
| return Api::NewHandle(Thread::Current(), library.ptr()); |
| } |
| |
| // --- Numbers, Integers and Doubles ---- |
| |
| DART_EXPORT Dart_Handle Dart_IntegerFitsIntoInt64(Dart_Handle integer, |
| bool* fits) { |
| // Fast path for Smis and Mints. |
| Thread* thread = Thread::Current(); |
| API_TIMELINE_DURATION(thread); |
| Isolate* isolate = thread->isolate(); |
| CHECK_ISOLATE(isolate); |
| if (Api::IsSmi(integer)) { |
| *fits = true; |
| return Api::Success(); |
| } |
| // Slow path for mints and type error. |
| DARTSCOPE(thread); |
| if (Api::ClassId(integer) == kMintCid) { |
| *fits = true; |
| return Api::Success(); |
| } |
| const Integer& int_obj = Api::UnwrapIntegerHandle(Z, integer); |
| ASSERT(int_obj.IsNull()); |
| RETURN_TYPE_ERROR(Z, integer, Integer); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_IntegerFitsIntoUint64(Dart_Handle integer, |
| bool* fits) { |
| // Fast path for Smis. |
| Thread* thread = Thread::Current(); |
| Isolate* isolate = thread->isolate(); |
| CHECK_ISOLATE(isolate); |
| API_TIMELINE_DURATION(thread); |
| if (Api::IsSmi(integer)) { |
| *fits = (Api::SmiValue(integer) >= 0); |
| return Api::Success(); |
| } |
| // Slow path for Mints. |
| DARTSCOPE(thread); |
| const Integer& int_obj = Api::UnwrapIntegerHandle(Z, integer); |
| if (int_obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, integer, Integer); |
| } |
| ASSERT(int_obj.IsMint()); |
| *fits = !int_obj.IsNegative(); |
| return Api::Success(); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_NewInteger(int64_t value) { |
| // Fast path for Smis. |
| Thread* thread = Thread::Current(); |
| Isolate* isolate = thread->isolate(); |
| CHECK_ISOLATE(isolate); |
| API_TIMELINE_DURATION(thread); |
| DARTSCOPE(thread); |
| CHECK_CALLBACK_STATE(thread); |
| return Api::NewHandle(thread, Integer::New(value)); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_NewIntegerFromUint64(uint64_t value) { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| API_TIMELINE_DURATION(T); |
| if (Integer::IsValueInRange(value)) { |
| return Api::NewHandle(T, Integer::NewFromUint64(value)); |
| } |
| return Api::NewError("%s: Cannot create Dart integer from value %" Pu64, |
| CURRENT_FUNC, value); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_NewIntegerFromHexCString(const char* str) { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| API_TIMELINE_DURATION(T); |
| const String& str_obj = String::Handle(Z, String::New(str)); |
| IntegerPtr integer = Integer::New(str_obj); |
| if (integer == Integer::null()) { |
| return Api::NewError("%s: Cannot create Dart integer from string %s", |
| CURRENT_FUNC, str); |
| } |
| return Api::NewHandle(T, integer); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_IntegerToInt64(Dart_Handle integer, |
| int64_t* value) { |
| // Fast path for Smis. |
| Thread* thread = Thread::Current(); |
| Isolate* isolate = thread->isolate(); |
| CHECK_ISOLATE(isolate); |
| if (Api::IsSmi(integer)) { |
| *value = Api::SmiValue(integer); |
| return Api::Success(); |
| } |
| // Slow path for Mints. |
| DARTSCOPE(thread); |
| const Integer& int_obj = Api::UnwrapIntegerHandle(Z, integer); |
| if (int_obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, integer, Integer); |
| } |
| ASSERT(int_obj.IsMint()); |
| *value = int_obj.AsInt64Value(); |
| return Api::Success(); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_IntegerToUint64(Dart_Handle integer, |
| uint64_t* value) { |
| // Fast path for Smis. |
| Thread* thread = Thread::Current(); |
| Isolate* isolate = thread->isolate(); |
| CHECK_ISOLATE(isolate); |
| if (Api::IsSmi(integer)) { |
| intptr_t smi_value = Api::SmiValue(integer); |
| if (smi_value >= 0) { |
| *value = smi_value; |
| return Api::Success(); |
| } |
| } |
| // Slow path for Mints. |
| DARTSCOPE(thread); |
| const Integer& int_obj = Api::UnwrapIntegerHandle(Z, integer); |
| if (int_obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, integer, Integer); |
| } |
| if (int_obj.IsSmi()) { |
| ASSERT(int_obj.IsNegative()); |
| } else { |
| ASSERT(int_obj.IsMint()); |
| if (!int_obj.IsNegative()) { |
| *value = int_obj.AsInt64Value(); |
| return Api::Success(); |
| } |
| } |
| return Api::NewError("%s: Integer %s cannot be represented as a uint64_t.", |
| CURRENT_FUNC, int_obj.ToCString()); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_IntegerToHexCString(Dart_Handle integer, |
| const char** value) { |
| DARTSCOPE(Thread::Current()); |
| API_TIMELINE_DURATION(T); |
| const Integer& int_obj = Api::UnwrapIntegerHandle(Z, integer); |
| if (int_obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, integer, Integer); |
| } |
| Zone* scope_zone = Api::TopScope(Thread::Current())->zone(); |
| *value = int_obj.ToHexCString(scope_zone); |
| return Api::Success(); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_NewDouble(double value) { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| return Api::NewHandle(T, Double::New(value)); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_DoubleValue(Dart_Handle double_obj, |
| double* value) { |
| DARTSCOPE(Thread::Current()); |
| const Double& obj = Api::UnwrapDoubleHandle(Z, double_obj); |
| if (obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, double_obj, Double); |
| } |
| *value = obj.value(); |
| return Api::Success(); |
| } |
| |
| DART_EXPORT Dart_Handle Dart_GetStaticMethodClosure(Dart_Handle library, |
| Dart_Handle cls_type, |
| Dart_Handle function_name) { |
| DARTSCOPE(Thread::Current()); |
| const Library& lib = Api::UnwrapLibraryHandle(Z, library); |
| if (lib.IsNull()) { |
| RETURN_TYPE_ERROR(Z, library, Library); |
| } |
| |
| const Type& type_obj = Api::UnwrapTypeHandle(Z, cls_type); |
| if (type_obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, cls_type, Type); |
| } |
| |
| const Class& klass = Class::Handle(Z, type_obj.type_class()); |
| if (klass.IsNull()) { |
| return Api::NewError( |
| "cls_type must be a Type object which represents a Class"); |
| } |
| |
| const auto& error = klass.EnsureIsFinalized(Thread::Current()); |
| |