| // 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_mirrors_api.h" |
| #include "include/dart_native_api.h" |
| |
| #include "platform/assert.h" |
| #include "vm/class_finalizer.h" |
| #include "vm/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/debuginfo.h" |
| #include "vm/exceptions.h" |
| #include "vm/flags.h" |
| #include "vm/growable_array.h" |
| #include "vm/lockers.h" |
| #include "vm/message.h" |
| #include "vm/message_handler.h" |
| #include "vm/native_entry.h" |
| #include "vm/object.h" |
| #include "vm/object_store.h" |
| #include "vm/os_thread.h" |
| #include "vm/os.h" |
| #include "vm/port.h" |
| #include "vm/precompiler.h" |
| #include "vm/profiler.h" |
| #include "vm/resolver.h" |
| #include "vm/reusable_handles.h" |
| #include "vm/service_event.h" |
| #include "vm/service_isolate.h" |
| #include "vm/service.h" |
| #include "vm/stack_frame.h" |
| #include "vm/symbols.h" |
| #include "vm/tags.h" |
| #include "vm/thread_registry.h" |
| #include "vm/timeline.h" |
| #include "vm/timer.h" |
| #include "vm/unicode.h" |
| #include "vm/verifier.h" |
| #include "vm/version.h" |
| |
| namespace dart { |
| |
| // Facilitate quick access to the current zone once we have the curren thread. |
| #define Z (T->zone()) |
| |
| |
| DECLARE_FLAG(bool, load_deferred_eagerly); |
| DECLARE_FLAG(bool, print_class_table); |
| DECLARE_FLAG(bool, verify_handles); |
| #if defined(DART_NO_SNAPSHOT) |
| DEFINE_FLAG(bool, check_function_fingerprints, true, |
| "Check function fingerprints"); |
| #endif // defined(DART_NO_SNAPSHOT). |
| DEFINE_FLAG(bool, trace_api, false, |
| "Trace invocation of API calls (debug mode only)"); |
| DEFINE_FLAG(bool, verify_acquired_data, false, |
| "Verify correct API acquire/release of typed data."); |
| |
| ThreadLocalKey Api::api_native_key_ = OSThread::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 FunctionVisitor : public ObjectVisitor { |
| public: |
| explicit FunctionVisitor(Thread* thread) : |
| ObjectVisitor(thread->isolate()), |
| classHandle_(Class::Handle(thread->zone())), |
| funcHandle_(Function::Handle(thread->zone())), |
| typeHandle_(AbstractType::Handle(thread->zone())) {} |
| |
| void VisitObject(RawObject* 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_.IsNull() || |
| !typeHandle_.IsResolved() || |
| 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_.IsResolved() || |
| typeHandle_.IsCanonical()); |
| } |
| } |
| } |
| |
| private: |
| Class& classHandle_; |
| Function& funcHandle_; |
| AbstractType& typeHandle_; |
| }; |
| #endif // #if defined(DEBUG). |
| |
| |
| static RawInstance* GetListInstance(Zone* zone, const Object& obj) { |
| if (obj.IsInstance()) { |
| const Library& core_lib = Library::Handle(zone, Library::CoreLibrary()); |
| const Class& list_class = |
| Class::Handle(zone, core_lib.LookupClass(Symbols::List())); |
| ASSERT(!list_class.IsNull()); |
| const Instance& instance = Instance::Cast(obj); |
| const Class& obj_class = Class::Handle(zone, obj.clazz()); |
| Error& malformed_type_error = Error::Handle(zone); |
| if (obj_class.IsSubtypeOf(Object::null_type_arguments(), |
| list_class, |
| Object::null_type_arguments(), |
| &malformed_type_error)) { |
| ASSERT(malformed_type_error.IsNull()); // Type is a raw List. |
| return instance.raw(); |
| } |
| } |
| return Instance::null(); |
| } |
| |
| static RawInstance* GetMapInstance(Zone* zone, const Object& obj) { |
| if (obj.IsInstance()) { |
| const Library& core_lib = Library::Handle(zone, Library::CoreLibrary()); |
| const Class& map_class = |
| Class::Handle(core_lib.LookupClass(Symbols::Map())); |
| ASSERT(!map_class.IsNull()); |
| const Instance& instance = Instance::Cast(obj); |
| const Class& obj_class = Class::Handle(zone, obj.clazz()); |
| Error& malformed_type_error = Error::Handle(zone); |
| if (obj_class.IsSubtypeOf(Object::null_type_arguments(), |
| map_class, |
| Object::null_type_arguments(), |
| &malformed_type_error)) { |
| ASSERT(malformed_type_error.IsNull()); // Type is a raw Map. |
| return instance.raw(); |
| } |
| } |
| return Instance::null(); |
| } |
| |
| |
| 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(); |
| Isolate* isolate = thread->isolate(); |
| ASSERT(isolate == Isolate::Current()); |
| *peer = NULL; |
| REUSABLE_OBJECT_HANDLESCOPE(thread); |
| Object& obj = thread->ObjectHandle(); |
| obj = arguments->NativeArgAt(arg_index); |
| if (RawObject::IsStringClassId(obj.GetClassId())) { |
| ASSERT(isolate->api_state() && |
| isolate->api_state()->top_scope() != NULL); |
| *str = Api::NewHandle(isolate, obj.raw()); |
| 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); |
| if (Api::GetNativeIntegerArgument(arguments, arg_index, value)) { |
| return true; |
| } |
| Thread* thread = arguments->thread(); |
| ASSERT(thread == Thread::Current()); |
| REUSABLE_OBJECT_HANDLESCOPE(thread); |
| Object& obj = thread->ObjectHandle(); |
| obj = arguments->NativeArgAt(arg_index); |
| intptr_t cid = obj.GetClassId(); |
| if (cid == kBigintCid) { |
| const Bigint& bigint = Bigint::Cast(obj); |
| if (bigint.FitsIntoInt64()) { |
| *value = bigint.AsInt64Value(); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| 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; |
| } |
| Thread* thread = arguments->thread(); |
| ASSERT(thread == Thread::Current()); |
| REUSABLE_OBJECT_HANDLESCOPE(thread); |
| Object& obj = thread->ObjectHandle(); |
| obj = arguments->NativeArgAt(arg_index); |
| intptr_t cid = obj.GetClassId(); |
| if (cid == kBigintCid) { |
| const Bigint& bigint = Bigint::Cast(obj); |
| if (bigint.FitsIntoUint64()) { |
| *value = bigint.AsUint64Value(); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| static bool GetNativeDoubleArgument(NativeArguments* arguments, |
| int arg_index, |
| double* value) { |
| ASSERT(value != NULL); |
| if (Api::GetNativeDoubleArgument(arguments, arg_index, value)) { |
| return true; |
| } |
| Thread* thread = arguments->thread(); |
| ASSERT(thread == Thread::Current()); |
| REUSABLE_OBJECT_HANDLESCOPE(thread); |
| Object& obj = thread->ObjectHandle(); |
| obj = arguments->NativeArgAt(arg_index); |
| intptr_t cid = obj.GetClassId(); |
| if (cid == kBigintCid) { |
| *value = Bigint::Cast(obj).AsDoubleValue(); |
| return true; |
| } |
| return false; |
| } |
| |
| |
| 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); |
| } |
| |
| |
| Heap::Space SpaceForExternal(Isolate* isolate, intptr_t size) { |
| Heap* heap = isolate->heap(); |
| // If 'size' would be a significant fraction of new space, then use old. |
| static const int kExtNewRatio = 16; |
| if (size > (heap->CapacityInWords(Heap::kNew) * kWordSize) / kExtNewRatio) { |
| return Heap::kOld; |
| } else { |
| return Heap::kNew; |
| } |
| } |
| |
| |
| static RawObject* Send0Arg(const Instance& receiver, |
| const String& selector) { |
| const intptr_t kNumArgs = 1; |
| ArgumentsDescriptor args_desc( |
| Array::Handle(ArgumentsDescriptor::New(kNumArgs))); |
| const Function& function = Function::Handle( |
| Resolver::ResolveDynamic(receiver, selector, args_desc)); |
| if (function.IsNull()) { |
| return ApiError::New(String::Handle(String::New(""))); |
| } |
| const Array& args = Array::Handle(Array::New(kNumArgs)); |
| args.SetAt(0, receiver); |
| return DartEntry::InvokeFunction(function, args); |
| } |
| |
| |
| static RawObject* Send1Arg(const Instance& receiver, |
| const String& selector, |
| const Instance& argument) { |
| const intptr_t kNumArgs = 2; |
| ArgumentsDescriptor args_desc( |
| Array::Handle(ArgumentsDescriptor::New(kNumArgs))); |
| const Function& function = Function::Handle( |
| Resolver::ResolveDynamic(receiver, selector, args_desc)); |
| if (function.IsNull()) { |
| return ApiError::New(String::Handle(String::New(""))); |
| } |
| const Array& args = Array::Handle(Array::New(kNumArgs)); |
| args.SetAt(0, receiver); |
| args.SetAt(1, argument); |
| return DartEntry::InvokeFunction(function, args); |
| } |
| |
| |
| WeakReferenceSetBuilder* ApiState::NewWeakReferenceSetBuilder() { |
| return new WeakReferenceSetBuilder(this); |
| } |
| |
| |
| void ApiState::DelayWeakReferenceSet(WeakReferenceSet* reference_set) { |
| WeakReferenceSet::Push(reference_set, &delayed_weak_reference_sets_); |
| } |
| |
| |
| Dart_Handle Api::InitNewHandle(Isolate* isolate, RawObject* raw) { |
| LocalHandles* local_handles = Api::TopScope(isolate)->local_handles(); |
| ASSERT(local_handles != NULL); |
| LocalHandle* ref = local_handles->AllocateHandle(); |
| ref->set_raw(raw); |
| return ref->apiHandle(); |
| } |
| |
| |
| Dart_Handle Api::NewHandle(Isolate* isolate, RawObject* raw) { |
| if (raw == Object::null()) { |
| return Null(); |
| } |
| if (raw == Bool::True().raw()) { |
| return True(); |
| } |
| if (raw == Bool::False().raw()) { |
| return False(); |
| } |
| return InitNewHandle(isolate, raw); |
| } |
| |
| |
| RawObject* Api::UnwrapHandle(Dart_Handle object) { |
| #if defined(DEBUG) |
| Isolate* isolate = Isolate::Current(); |
| ASSERT(isolate != NULL); |
| ApiState* state = isolate->api_state(); |
| ASSERT(state != NULL); |
| ASSERT(!FLAG_verify_handles || |
| state->IsValidLocalHandle(object) || |
| Dart::IsReadOnlyApiHandle(object)); |
| ASSERT(FinalizablePersistentHandle::raw_offset() == 0 && |
| PersistentHandle::raw_offset() == 0 && |
| LocalHandle::raw_offset() == 0); |
| #endif |
| return (reinterpret_cast<LocalHandle*>(object))->raw(); |
| } |
| |
| |
| #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(Isolate* isolate) { |
| if (!isolate->AllowClassFinalization()) { |
| // Class finalization is blocked for the isolate. Do nothing. |
| return Api::Success(); |
| } |
| if (ClassFinalizer::ProcessPendingClasses()) { |
| return Api::Success(); |
| } |
| ASSERT(isolate->object_store()->sticky_error() != Object::null()); |
| return Api::NewHandle(isolate, isolate->object_store()->sticky_error()); |
| } |
| |
| |
| Dart_Isolate Api::CastIsolate(Isolate* isolate) { |
| return reinterpret_cast<Dart_Isolate>(isolate); |
| } |
| |
| |
| Dart_Handle Api::NewError(const char* format, ...) { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| |
| va_list args; |
| va_start(args, format); |
| intptr_t len = OS::VSNPrint(NULL, 0, format, args); |
| va_end(args); |
| |
| char* buffer = Z->Alloc<char>(len + 1); |
| va_list args2; |
| va_start(args2, format); |
| OS::VSNPrint(buffer, (len + 1), format, args2); |
| va_end(args2); |
| |
| const String& message = String::Handle(Z, String::New(buffer)); |
| return Api::NewHandle(I, ApiError::New(message)); |
| } |
| |
| |
| void Api::SetupAcquiredError(Isolate* isolate) { |
| ASSERT(isolate != NULL); |
| ApiState* state = isolate->api_state(); |
| ASSERT(state != NULL); |
| state->SetupAcquiredError(); |
| } |
| |
| |
| Dart_Handle Api::AcquiredError(Isolate* isolate) { |
| ASSERT(isolate != NULL); |
| ApiState* state = isolate->api_state(); |
| ASSERT(state != NULL); |
| PersistentHandle* acquired_error_handle = state->AcquiredError(); |
| return reinterpret_cast<Dart_Handle>(acquired_error_handle); |
| } |
| |
| |
| ApiLocalScope* Api::TopScope(Isolate* isolate) { |
| ASSERT(isolate != NULL); |
| ApiState* state = isolate->api_state(); |
| ASSERT(state != NULL); |
| ApiLocalScope* scope = state->top_scope(); |
| ASSERT(scope != NULL); |
| return scope; |
| } |
| |
| |
| void Api::InitOnce() { |
| ASSERT(api_native_key_ == OSThread::kUnsetThreadLocalKey); |
| api_native_key_ = OSThread::CreateThreadLocal(); |
| ASSERT(api_native_key_ != OSThread::kUnsetThreadLocalKey); |
| } |
| |
| |
| static Dart_Handle InitNewReadOnlyApiHandle(RawObject* raw) { |
| ASSERT(raw->IsVMHeapObject()); |
| LocalHandle* ref = Dart::AllocateReadOnlyApiHandle(); |
| ref->set_raw(raw); |
| return ref->apiHandle(); |
| } |
| |
| |
| void Api::InitHandles() { |
| Isolate* isolate = Isolate::Current(); |
| ASSERT(isolate != NULL); |
| ASSERT(isolate == Dart::vm_isolate()); |
| ApiState* state = isolate->api_state(); |
| ASSERT(state != NULL); |
| |
| ASSERT(true_handle_ == NULL); |
| true_handle_ = InitNewReadOnlyApiHandle(Bool::True().raw()); |
| |
| ASSERT(false_handle_ == NULL); |
| false_handle_ = InitNewReadOnlyApiHandle(Bool::False().raw()); |
| |
| ASSERT(null_handle_ == NULL); |
| null_handle_ = InitNewReadOnlyApiHandle(Object::null()); |
| |
| ASSERT(empty_string_handle_ == NULL); |
| empty_string_handle_ = InitNewReadOnlyApiHandle(Symbols::Empty().raw()); |
| } |
| |
| |
| bool Api::StringGetPeerHelper(NativeArguments* arguments, |
| int arg_index, |
| void** peer) { |
| NoSafepointScope no_safepoint_scope; |
| RawObject* raw_obj = arguments->NativeArgAt(arg_index); |
| if (!raw_obj->IsHeapObject()) { |
| return false; |
| } |
| intptr_t cid = raw_obj->GetClassId(); |
| if (cid == kExternalOneByteStringCid) { |
| RawExternalOneByteString* raw_string = |
| reinterpret_cast<RawExternalOneByteString*>(raw_obj)->ptr(); |
| ExternalStringData<uint8_t>* data = raw_string->external_data_; |
| *peer = data->peer(); |
| return true; |
| } |
| if (cid == kOneByteStringCid || cid == kTwoByteStringCid) { |
| Isolate* isolate = arguments->thread()->isolate(); |
| *peer = isolate->heap()->GetPeer(raw_obj); |
| return (*peer != 0); |
| } |
| if (cid == kExternalTwoByteStringCid) { |
| RawExternalTwoByteString* raw_string = |
| reinterpret_cast<RawExternalTwoByteString*>(raw_obj)->ptr(); |
| ExternalStringData<uint16_t>* data = raw_string->external_data_; |
| *peer = data->peer(); |
| return true; |
| } |
| return false; |
| } |
| |
| |
| bool Api::GetNativeReceiver(NativeArguments* arguments, intptr_t* value) { |
| NoSafepointScope no_safepoint_scope; |
| RawObject* 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)); |
| RawTypedData* native_fields = *reinterpret_cast<RawTypedData**>( |
| RawObject::ToAddr(raw_obj) + sizeof(RawObject)); |
| if (native_fields == TypedData::null()) { |
| *value = 0; |
| } else { |
| *value = *bit_cast<intptr_t*, uint8_t*>(native_fields->ptr()->data()); |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| bool Api::GetNativeBooleanArgument(NativeArguments* arguments, |
| int arg_index, |
| bool* value) { |
| NoSafepointScope no_safepoint_scope; |
| RawObject* 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().raw()); |
| 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; |
| RawObject* raw_obj = arguments->NativeArgAt(arg_index); |
| if (raw_obj->IsHeapObject()) { |
| intptr_t cid = raw_obj->GetClassId(); |
| if (cid == kMintCid) { |
| *value = reinterpret_cast<RawMint*>(raw_obj)->ptr()->value_; |
| return true; |
| } |
| return false; |
| } |
| *value = Smi::Value(reinterpret_cast<RawSmi*>(raw_obj)); |
| return true; |
| } |
| |
| |
| bool Api::GetNativeDoubleArgument(NativeArguments* arguments, |
| int arg_index, |
| double* value) { |
| NoSafepointScope no_safepoint_scope; |
| RawObject* raw_obj = arguments->NativeArgAt(arg_index); |
| if (raw_obj->IsHeapObject()) { |
| intptr_t cid = raw_obj->GetClassId(); |
| if (cid == kDoubleCid) { |
| *value = reinterpret_cast<RawDouble*>(raw_obj)->ptr()->value_; |
| return true; |
| } |
| if (cid == kMintCid) { |
| *value = static_cast<double>( |
| reinterpret_cast<RawMint*>(raw_obj)->ptr()->value_); |
| return true; |
| } |
| return false; |
| } |
| *value = static_cast<double>(Smi::Value(reinterpret_cast<RawSmi*>(raw_obj))); |
| return true; |
| } |
| |
| |
| bool Api::GetNativeFieldsOfArgument(NativeArguments* arguments, |
| int arg_index, |
| int num_fields, |
| intptr_t* field_values) { |
| NoSafepointScope no_safepoint_scope; |
| RawObject* raw_obj = arguments->NativeArgAt(arg_index); |
| if (raw_obj->IsHeapObject()) { |
| intptr_t cid = raw_obj->GetClassId(); |
| if (cid > kNumPredefinedCids) { |
| RawTypedData* native_fields = *reinterpret_cast<RawTypedData**>( |
| RawObject::ToAddr(raw_obj) + sizeof(RawObject)); |
| if (native_fields == TypedData::null()) { |
| memset(field_values, 0, (num_fields * sizeof(field_values[0]))); |
| } else if (num_fields == Smi::Value(native_fields->ptr()->length_)) { |
| intptr_t* native_values = |
| bit_cast<intptr_t*, uint8_t*>(native_fields->ptr()->data()); |
| memmove(field_values, |
| native_values, |
| (num_fields * sizeof(field_values[0]))); |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| void Api::SetWeakHandleReturnValue(NativeArguments* args, |
| Dart_WeakPersistentHandle retval) { |
| args->SetReturnUnsafe(FinalizablePersistentHandle::Cast(retval)->raw()); |
| } |
| |
| |
| PersistentHandle* PersistentHandle::Cast(Dart_PersistentHandle handle) { |
| ASSERT(Isolate::Current()->api_state()->IsValidPersistentHandle(handle)); |
| return reinterpret_cast<PersistentHandle*>(handle); |
| } |
| |
| |
| FinalizablePersistentHandle* FinalizablePersistentHandle::Cast( |
| Dart_WeakPersistentHandle handle) { |
| #if defined(DEBUG) |
| ApiState* state = Isolate::Current()->api_state(); |
| ASSERT(state->IsValidWeakPersistentHandle(handle) || |
| state->IsValidPrologueWeakPersistentHandle(handle)); |
| #endif |
| return reinterpret_cast<FinalizablePersistentHandle*>(handle); |
| } |
| |
| |
| void FinalizablePersistentHandle::Finalize( |
| Isolate* isolate, FinalizablePersistentHandle* handle) { |
| if (!handle->raw()->IsHeapObject()) { |
| return; |
| } |
| Dart_WeakPersistentHandleFinalizer callback = handle->callback(); |
| ASSERT(callback != NULL); |
| void* peer = handle->peer(); |
| Dart_WeakPersistentHandle object = handle->apiHandle(); |
| (*callback)(isolate->init_callback_data(), object, peer); |
| ApiState* state = isolate->api_state(); |
| ASSERT(state != NULL); |
| if (handle->IsPrologueWeakPersistent()) { |
| state->prologue_weak_persistent_handles().FreeHandle(handle); |
| } else { |
| state->weak_persistent_handles().FreeHandle(handle); |
| } |
| } |
| |
| |
| // --- Handles --- |
| |
| DART_EXPORT bool Dart_IsError(Dart_Handle handle) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return RawObject::IsErrorClassId(Api::ClassId(handle)); |
| } |
| |
| |
| DART_EXPORT bool Dart_IsApiError(Dart_Handle object) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return Api::ClassId(object) == kApiErrorCid; |
| } |
| |
| |
| DART_EXPORT bool Dart_IsUnhandledExceptionError(Dart_Handle object) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return Api::ClassId(object) == kUnhandledExceptionCid; |
| } |
| |
| |
| DART_EXPORT bool Dart_IsCompilationError(Dart_Handle object) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return Api::ClassId(object) == kLanguageErrorCid; |
| } |
| |
| |
| DART_EXPORT bool Dart_IsFatalError(Dart_Handle object) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return Api::ClassId(object) == kUnwindErrorCid; |
| } |
| |
| |
| DART_EXPORT bool Dart_IsVMRestartRequest(Dart_Handle handle) { |
| DARTSCOPE(Thread::Current()); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(handle)); |
| return (obj.IsUnwindError() && UnwindError::Cast(obj).is_vm_restart()); |
| } |
| |
| |
| DART_EXPORT const char* Dart_GetError(Dart_Handle handle) { |
| DARTSCOPE(Thread::Current()); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(handle)); |
| if (obj.IsError()) { |
| const Error& error = Error::Cast(obj); |
| const char* str = error.ToErrorCString(); |
| intptr_t len = strlen(str) + 1; |
| char* str_copy = Api::TopScope(I)->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_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(I, 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(I, 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."); |
| } |
| } |
| |
| |
| // TODO(turnidge): This clones Api::NewError. I need to use va_copy to |
| // fix this but not sure if it available on all of our builds. |
| 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(I, ApiError::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)) { |
| obj = String::New(::Dart_GetError(exception)); |
| } else { |
| obj = Api::UnwrapInstanceHandle(Z, exception).raw(); |
| if (obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, exception, Instance); |
| } |
| } |
| const Stacktrace& stacktrace = Stacktrace::Handle(Z); |
| return Api::NewHandle(I, UnhandledException::New(obj, stacktrace)); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_PropagateError(Dart_Handle handle) { |
| Thread* thread = Thread::Current(); |
| Isolate* isolate = thread->isolate(); |
| { |
| const Object& obj = Object::Handle(thread->zone(), |
| Api::UnwrapHandle(handle)); |
| if (!obj.IsError()) { |
| return Api::NewError( |
| "%s expects argument 'handle' to be an error handle. " |
| "Did you forget to check Dart_IsError first?", |
| CURRENT_FUNC); |
| } |
| } |
| if (isolate->top_exit_frame_info() == 0) { |
| // There are no dart frames on the stack so it would be illegal to |
| // propagate an error here. |
| return Api::NewError("No Dart frames on stack, cannot propagate error."); |
| } |
| |
| // Unwind all the API scopes till the exit frame before propagating. |
| ApiState* state = isolate->api_state(); |
| ASSERT(state != NULL); |
| 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; |
| RawError* raw_error = Api::UnwrapErrorHandle(thread->zone(), handle).raw(); |
| state->UnwindScopes(isolate->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(); |
| return Api::NewError("Cannot reach here. Internal error."); |
| } |
| |
| |
| DART_EXPORT void _Dart_ReportErrorHandle(const char* file, |
| int line, |
| const char* handle, |
| const char* message) { |
| fprintf(stderr, "%s:%d: error handle: '%s':\n '%s'\n", |
| file, line, handle, message); |
| OS::Abort(); |
| } |
| |
| |
| 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(I, obj.raw()); |
| } else if (obj.IsInstance()) { |
| CHECK_CALLBACK_STATE(T); |
| const Instance& receiver = Instance::Cast(obj); |
| return Api::NewHandle(I, DartLibraryCalls::ToString(receiver)); |
| } else { |
| CHECK_CALLBACK_STATE(T); |
| // This is a VM internal object. Call the C++ method of printing. |
| return Api::NewHandle(I, 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 uint64_t Dart_IdentityHash(Dart_Handle obj) { |
| DARTSCOPE(Thread::Current()); |
| |
| const Object& object = Object::Handle(Z, Api::UnwrapHandle(obj)); |
| if (!object.IsInstance() && !object.IsNull()) { |
| return 0; |
| } |
| |
| const Library& libcore = Library::Handle(Z, Library::CoreLibrary()); |
| const String& function_name = String::Handle(Z, |
| String::New("identityHashCode")); |
| const Function& function = |
| Function::Handle(Z, libcore.LookupFunctionAllowPrivate(function_name)); |
| if (function.IsNull()) { |
| UNREACHABLE(); |
| return 0; |
| } |
| |
| const Array& arguments = Array::Handle(Z, Array::New(1)); |
| arguments.SetAt(0, object); |
| const Object& result = |
| Object::Handle(Z, DartEntry::InvokeFunction(function, arguments)); |
| |
| if (result.IsSmi()) { |
| return Smi::Cast(result).Value(); |
| } |
| if (result.IsMint()) { |
| const Mint& mint = Mint::Cast(result); |
| if (!mint.IsNegative()) { |
| return mint.AsInt64Value(); |
| } |
| } |
| if (result.IsBigint()) { |
| const Bigint& bigint = Bigint::Cast(result); |
| if (bigint.FitsIntoUint64()) { |
| return bigint.AsUint64Value(); |
| } |
| } |
| return 0; |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_HandleFromPersistent( |
| Dart_PersistentHandle object) { |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| ApiState* state = isolate->api_state(); |
| ASSERT(state != NULL); |
| PersistentHandle* ref = PersistentHandle::Cast(object); |
| return Api::NewHandle(isolate, ref->raw()); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_HandleFromWeakPersistent( |
| Dart_WeakPersistentHandle object) { |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| ApiState* state = isolate->api_state(); |
| ASSERT(state != NULL); |
| FinalizablePersistentHandle* weak_ref = |
| FinalizablePersistentHandle::Cast(object); |
| return Api::NewHandle(isolate, weak_ref->raw()); |
| } |
| |
| |
| DART_EXPORT Dart_PersistentHandle Dart_NewPersistentHandle(Dart_Handle object) { |
| DARTSCOPE(Thread::Current()); |
| ApiState* state = I->api_state(); |
| ASSERT(state != NULL); |
| const Object& old_ref = Object::Handle(Z, Api::UnwrapHandle(object)); |
| PersistentHandle* new_ref = state->persistent_handles().AllocateHandle(); |
| new_ref->set_raw(old_ref); |
| return new_ref->apiHandle(); |
| } |
| |
| |
| DART_EXPORT void Dart_SetPersistentHandle(Dart_PersistentHandle obj1, |
| Dart_Handle obj2) { |
| DARTSCOPE(Thread::Current()); |
| ApiState* state = I->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_raw(obj2_ref); |
| } |
| |
| |
| static Dart_WeakPersistentHandle AllocateFinalizableHandle( |
| Thread* thread, |
| Dart_Handle object, |
| bool is_prologue, |
| void* peer, |
| intptr_t external_allocation_size, |
| Dart_WeakPersistentHandleFinalizer callback) { |
| REUSABLE_OBJECT_HANDLESCOPE(thread); |
| Object& ref = thread->ObjectHandle(); |
| ref = Api::UnwrapHandle(object); |
| FinalizablePersistentHandle* finalizable_ref = |
| FinalizablePersistentHandle::New(thread->isolate(), |
| is_prologue, |
| ref, |
| peer, |
| callback, |
| external_allocation_size); |
| return finalizable_ref->apiHandle(); |
| } |
| |
| |
| DART_EXPORT Dart_WeakPersistentHandle Dart_NewWeakPersistentHandle( |
| Dart_Handle object, |
| void* peer, |
| intptr_t external_allocation_size, |
| Dart_WeakPersistentHandleFinalizer callback) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| if (callback == NULL) { |
| return NULL; |
| } |
| return AllocateFinalizableHandle(thread, |
| object, |
| false, |
| peer, |
| external_allocation_size, |
| callback); |
| } |
| |
| |
| DART_EXPORT Dart_WeakPersistentHandle Dart_NewPrologueWeakPersistentHandle( |
| Dart_Handle object, |
| void* peer, |
| intptr_t external_allocation_size, |
| Dart_WeakPersistentHandleFinalizer callback) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| if (callback == NULL) { |
| return NULL; |
| } |
| return AllocateFinalizableHandle(thread, |
| object, |
| true, |
| peer, |
| external_allocation_size, |
| callback); |
| } |
| |
| |
| DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object) { |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| ApiState* state = isolate->api_state(); |
| ASSERT(state != NULL); |
| PersistentHandle* ref = PersistentHandle::Cast(object); |
| ASSERT(!state->IsProtectedHandle(ref)); |
| if (!state->IsProtectedHandle(ref)) { |
| state->persistent_handles().FreeHandle(ref); |
| } |
| } |
| |
| |
| DART_EXPORT void Dart_DeleteWeakPersistentHandle( |
| Dart_Isolate current_isolate, |
| Dart_WeakPersistentHandle object) { |
| Isolate* isolate = reinterpret_cast<Isolate*>(current_isolate); |
| CHECK_ISOLATE(isolate); |
| ASSERT(isolate == Isolate::Current()); |
| ApiState* state = isolate->api_state(); |
| ASSERT(state != NULL); |
| FinalizablePersistentHandle* weak_ref = |
| FinalizablePersistentHandle::Cast(object); |
| weak_ref->EnsureFreeExternal(isolate); |
| if (weak_ref->IsPrologueWeakPersistent()) { |
| ASSERT(state->IsValidPrologueWeakPersistentHandle(object)); |
| state->prologue_weak_persistent_handles().FreeHandle(weak_ref); |
| } else { |
| ASSERT(!state->IsValidPrologueWeakPersistentHandle(object)); |
| state->weak_persistent_handles().FreeHandle(weak_ref); |
| } |
| } |
| |
| |
| DART_EXPORT bool Dart_IsPrologueWeakPersistentHandle( |
| Dart_WeakPersistentHandle object) { |
| FinalizablePersistentHandle* weak_ref = |
| FinalizablePersistentHandle::Cast(object); |
| return weak_ref->IsPrologueWeakPersistent(); |
| } |
| |
| |
| DART_EXPORT Dart_WeakReferenceSetBuilder Dart_NewWeakReferenceSetBuilder() { |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| ApiState* state = isolate->api_state(); |
| ASSERT(state != NULL); |
| return reinterpret_cast<Dart_WeakReferenceSetBuilder>( |
| state->NewWeakReferenceSetBuilder()); |
| } |
| |
| |
| DART_EXPORT Dart_WeakReferenceSet Dart_NewWeakReferenceSet( |
| Dart_WeakReferenceSetBuilder set_builder, |
| Dart_WeakPersistentHandle key, |
| Dart_WeakPersistentHandle value) { |
| ASSERT(set_builder != NULL && key != NULL); |
| WeakReferenceSetBuilder* builder = |
| reinterpret_cast<WeakReferenceSetBuilder*>(set_builder); |
| ApiState* state = builder->api_state(); |
| ASSERT(state == Isolate::Current()->api_state()); |
| WeakReferenceSet* reference_set = builder->NewWeakReferenceSet(); |
| reference_set->AppendKey(key); |
| if (value != NULL) { |
| reference_set->AppendValue(value); |
| } |
| state->DelayWeakReferenceSet(reference_set); |
| return reinterpret_cast<Dart_WeakReferenceSet>(reference_set); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_AppendToWeakReferenceSet( |
| Dart_WeakReferenceSet reference_set, |
| Dart_WeakPersistentHandle key, |
| Dart_WeakPersistentHandle value) { |
| ASSERT(reference_set != NULL); |
| WeakReferenceSet* set = reinterpret_cast<WeakReferenceSet*>(reference_set); |
| set->Append(key, value); |
| return Api::Success(); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_AppendKeyToWeakReferenceSet( |
| Dart_WeakReferenceSet reference_set, |
| Dart_WeakPersistentHandle key) { |
| ASSERT(reference_set != NULL); |
| WeakReferenceSet* set = reinterpret_cast<WeakReferenceSet*>(reference_set); |
| set->AppendKey(key); |
| return Api::Success(); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_AppendValueToWeakReferenceSet( |
| Dart_WeakReferenceSet reference_set, |
| Dart_WeakPersistentHandle value) { |
| ASSERT(reference_set != NULL); |
| WeakReferenceSet* set = reinterpret_cast<WeakReferenceSet*>(reference_set); |
| set->AppendValue(value); |
| return Api::Success(); |
| } |
| |
| |
| // --- Garbage Collection Callbacks -- |
| |
| DART_EXPORT Dart_Handle Dart_SetGcCallbacks( |
| Dart_GcPrologueCallback prologue_callback, |
| Dart_GcEpilogueCallback epilogue_callback) { |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| if (prologue_callback != NULL) { |
| if (isolate->gc_prologue_callback() != NULL) { |
| return Api::NewError( |
| "%s permits only one gc prologue callback to be registered, please " |
| "remove the existing callback and then add this callback", |
| CURRENT_FUNC); |
| } |
| } else { |
| if (isolate->gc_prologue_callback() == NULL) { |
| return Api::NewError( |
| "%s expects 'prologue_callback' to be present in the callback set.", |
| CURRENT_FUNC); |
| } |
| } |
| if (epilogue_callback != NULL) { |
| if (isolate->gc_epilogue_callback() != NULL) { |
| return Api::NewError( |
| "%s permits only one gc epilogue callback to be registered, please " |
| "remove the existing callback and then add this callback", |
| CURRENT_FUNC); |
| } |
| } else { |
| if (isolate->gc_epilogue_callback() == NULL) { |
| return Api::NewError( |
| "%s expects 'epilogue_callback' to be present in the callback set.", |
| CURRENT_FUNC); |
| } |
| } |
| isolate->set_gc_prologue_callback(prologue_callback); |
| isolate->set_gc_epilogue_callback(epilogue_callback); |
| return Api::Success(); |
| } |
| |
| |
| class PrologueWeakVisitor : public HandleVisitor { |
| public: |
| PrologueWeakVisitor(Thread* thread, |
| Dart_GcPrologueWeakHandleCallback callback) |
| : HandleVisitor(thread), |
| callback_(callback) { |
| } |
| |
| |
| void VisitHandle(uword addr) { |
| NoSafepointScope no_safepoint; |
| FinalizablePersistentHandle* handle = |
| reinterpret_cast<FinalizablePersistentHandle*>(addr); |
| RawObject* raw_obj = handle->raw(); |
| if (raw_obj->IsHeapObject()) { |
| ASSERT(handle->IsPrologueWeakPersistent()); |
| ReusableInstanceHandleScope reused_instance_handle(thread()); |
| Instance& instance = reused_instance_handle.Handle(); |
| instance ^= reinterpret_cast<RawInstance*>(handle->raw()); |
| intptr_t num_native_fields = instance.NumNativeFields(); |
| intptr_t* native_fields = instance.NativeFieldsDataAddr(); |
| if (native_fields != NULL) { |
| callback_(thread()->isolate()->init_callback_data(), |
| reinterpret_cast<Dart_WeakPersistentHandle>(addr), |
| num_native_fields, |
| native_fields); |
| } |
| } |
| } |
| |
| private: |
| Dart_GcPrologueWeakHandleCallback callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PrologueWeakVisitor); |
| }; |
| |
| |
| DART_EXPORT Dart_Handle Dart_VisitPrologueWeakHandles( |
| Dart_GcPrologueWeakHandleCallback callback) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| PrologueWeakVisitor visitor(thread, callback); |
| thread->isolate()->VisitPrologueWeakPersistentHandles(&visitor); |
| return Api::Success(); |
| } |
| |
| |
| // --- Initialization and Globals --- |
| |
| DART_EXPORT const char* Dart_VersionString() { |
| return Version::String(); |
| } |
| |
| DART_EXPORT char* Dart_Initialize( |
| const uint8_t* vm_isolate_snapshot, |
| const uint8_t* instructions_snapshot, |
| Dart_IsolateCreateCallback create, |
| Dart_IsolateInterruptCallback interrupt, |
| Dart_IsolateUnhandledExceptionCallback unhandled, |
| Dart_IsolateShutdownCallback shutdown, |
| Dart_FileOpenCallback file_open, |
| Dart_FileReadCallback file_read, |
| Dart_FileWriteCallback file_write, |
| Dart_FileCloseCallback file_close, |
| Dart_EntropySource entropy_source) { |
| const char* err_msg = Dart::InitOnce(vm_isolate_snapshot, |
| instructions_snapshot, |
| create, interrupt, unhandled, shutdown, |
| file_open, file_read, file_write, |
| file_close, entropy_source); |
| if (err_msg != NULL) { |
| return strdup(err_msg); |
| } |
| return NULL; |
| } |
| |
| |
| DART_EXPORT char* Dart_Cleanup() { |
| CHECK_NO_ISOLATE(Isolate::Current()); |
| const char* err_msg = Dart::Cleanup(); |
| if (err_msg != NULL) { |
| return strdup(err_msg); |
| } |
| return NULL; |
| } |
| |
| |
| DART_EXPORT bool 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); |
| } |
| |
| |
| // --- Isolates --- |
| |
| static char* BuildIsolateName(const char* script_uri, |
| const char* main) { |
| if (script_uri == NULL) { |
| // Just use the main as the name. |
| if (main == NULL) { |
| return strdup("isolate"); |
| } else { |
| return strdup(main); |
| } |
| } |
| |
| if (ServiceIsolate::NameEquals(script_uri)) { |
| return strdup(script_uri); |
| } |
| |
| // Skip past any slashes and backslashes in the script uri. |
| const char* last_slash = strrchr(script_uri, '/'); |
| if (last_slash != NULL) { |
| script_uri = last_slash + 1; |
| } |
| const char* last_backslash = strrchr(script_uri, '\\'); |
| if (last_backslash != NULL) { |
| script_uri = last_backslash + 1; |
| } |
| if (main == NULL) { |
| main = "main"; |
| } |
| |
| char* chars = NULL; |
| intptr_t len = OS::SNPrint(NULL, 0, "%s$%s", script_uri, main) + 1; |
| chars = reinterpret_cast<char*>(malloc(len)); |
| OS::SNPrint(chars, len, "%s$%s", script_uri, main); |
| return chars; |
| } |
| |
| |
| DART_EXPORT Dart_Isolate Dart_CreateIsolate(const char* script_uri, |
| const char* main, |
| const uint8_t* snapshot, |
| Dart_IsolateFlags* flags, |
| void* callback_data, |
| char** error) { |
| CHECK_NO_ISOLATE(Isolate::Current()); |
| char* isolate_name = BuildIsolateName(script_uri, main); |
| Thread::EnsureInit(); |
| |
| // Setup default flags in case none were passed. |
| Dart_IsolateFlags api_flags; |
| if (flags == NULL) { |
| Isolate::Flags vm_flags; |
| vm_flags.CopyTo(&api_flags); |
| flags = &api_flags; |
| } |
| Isolate* I = Dart::CreateIsolate(isolate_name, *flags); |
| free(isolate_name); |
| if (I == NULL) { |
| *error = strdup("Isolate creation failed"); |
| return reinterpret_cast<Dart_Isolate>(NULL); |
| } |
| { |
| Thread* T = Thread::Current(); |
| 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. |
| Dart_EnterScope(); |
| const Error& error_obj = |
| Error::Handle(Z, Dart::InitializeIsolate(snapshot, callback_data)); |
| if (error_obj.IsNull()) { |
| #if defined(DART_NO_SNAPSHOT) |
| if (FLAG_check_function_fingerprints) { |
| Library::CheckFunctionFingerprints(); |
| } |
| #endif // defined(DART_NO_SNAPSHOT). |
| // We exit the API scope entered above. |
| Dart_ExitScope(); |
| return Api::CastIsolate(I); |
| } |
| *error = strdup(error_obj.ToErrorCString()); |
| // We exit the API scope entered above. |
| Dart_ExitScope(); |
| } |
| Dart::ShutdownIsolate(); |
| return reinterpret_cast<Dart_Isolate>(NULL); |
| } |
| |
| |
| DART_EXPORT void Dart_ShutdownIsolate() { |
| Thread* T = Thread::Current(); |
| Isolate* I = T->isolate(); |
| CHECK_ISOLATE(I); |
| { |
| StackZone zone(T); |
| HandleScope handle_scope(T); |
| 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); |
| return isolate->init_callback_data(); |
| } |
| |
| |
| DART_EXPORT void* Dart_IsolateData(Dart_Isolate isolate) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| if (isolate == NULL) { |
| FATAL1("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC); |
| } |
| // TODO(16615): Validate isolate parameter. |
| Isolate* iso = reinterpret_cast<Isolate*>(isolate); |
| return iso->init_callback_data(); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_DebugName() { |
| DARTSCOPE(Thread::Current()); |
| return Api::NewHandle(I, String::New(I->name())); |
| } |
| |
| |
| |
| DART_EXPORT void Dart_EnterIsolate(Dart_Isolate isolate) { |
| CHECK_NO_ISOLATE(Isolate::Current()); |
| // TODO(16615): Validate isolate parameter. |
| Isolate* iso = reinterpret_cast<Isolate*>(isolate); |
| if (iso->HasMutatorThread()) { |
| FATAL("Multiple mutators within one isolate is not supported."); |
| } |
| Thread::EnsureInit(); |
| Thread::EnterIsolate(iso); |
| } |
| |
| |
| DART_EXPORT void Dart_IsolateBlocked() { |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| if (profiler_data == NULL) { |
| return; |
| } |
| profiler_data->Block(); |
| } |
| |
| |
| DART_EXPORT void Dart_IsolateUnblocked() { |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| if (profiler_data == NULL) { |
| return; |
| } |
| profiler_data->Unblock(); |
| } |
| |
| |
| DART_EXPORT void Dart_ExitIsolate() { |
| CHECK_ISOLATE(Isolate::Current()); |
| Thread::ExitIsolate(); |
| } |
| |
| |
| // TODO(iposva): Remove this API and instead expose the underlying flags. |
| DART_EXPORT Dart_Handle Dart_IsolateSetStrictCompilation(bool value) { |
| CHECK_ISOLATE(Isolate::Current()); |
| Isolate* isolate = Isolate::Current(); |
| if (isolate->has_compiled_code()) { |
| return Api::NewError( |
| "%s expects that the isolate has not yet compiled code.", CURRENT_FUNC); |
| } |
| if (!value) { |
| return Api::NewError( |
| "%s expects that the value is set to true only.", CURRENT_FUNC); |
| } |
| Isolate::Current()->set_strict_compilation(); |
| return Api::Null(); |
| } |
| |
| |
| static uint8_t* ApiReallocate(uint8_t* ptr, |
| intptr_t old_size, |
| intptr_t new_size) { |
| return Api::TopScope(Isolate::Current())->zone()->Realloc<uint8_t>( |
| ptr, old_size, new_size); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_CreateSnapshot( |
| uint8_t** vm_isolate_snapshot_buffer, |
| intptr_t* vm_isolate_snapshot_size, |
| uint8_t** isolate_snapshot_buffer, |
| intptr_t* isolate_snapshot_size) { |
| ASSERT(FLAG_load_deferred_eagerly); |
| DARTSCOPE(Thread::Current()); |
| if (vm_isolate_snapshot_buffer != NULL && |
| vm_isolate_snapshot_size == NULL) { |
| RETURN_NULL_ERROR(vm_isolate_snapshot_size); |
| } |
| if (isolate_snapshot_buffer == NULL) { |
| RETURN_NULL_ERROR(isolate_snapshot_buffer); |
| } |
| if (isolate_snapshot_size == NULL) { |
| RETURN_NULL_ERROR(isolate_snapshot_size); |
| } |
| // Finalize all classes if needed. |
| Dart_Handle state = Api::CheckAndFinalizePendingClasses(I); |
| if (::Dart_IsError(state)) { |
| return state; |
| } |
| I->heap()->CollectAllGarbage(); |
| #if defined(DEBUG) |
| FunctionVisitor check_canonical(T); |
| I->heap()->IterateObjects(&check_canonical); |
| #endif // #if defined(DEBUG). |
| |
| // Since this is only a snapshot the root library should not be set. |
| I->object_store()->set_root_library(Library::Handle(Z)); |
| FullSnapshotWriter writer(vm_isolate_snapshot_buffer, |
| isolate_snapshot_buffer, |
| NULL, /* instructions_snapshot_buffer */ |
| ApiReallocate, |
| false, /* snapshot_code */ |
| true /* vm_isolate_is_symbolic */); |
| writer.WriteFullSnapshot(); |
| *vm_isolate_snapshot_size = writer.VmIsolateSnapshotSize(); |
| *isolate_snapshot_size = writer.IsolateSnapshotSize(); |
| return Api::Success(); |
| } |
| |
| |
| static Dart_Handle createLibrarySnapshot(Dart_Handle library, |
| uint8_t** buffer, |
| intptr_t* size) { |
| DARTSCOPE(Thread::Current()); |
| if (buffer == NULL) { |
| RETURN_NULL_ERROR(buffer); |
| } |
| if (size == NULL) { |
| RETURN_NULL_ERROR(size); |
| } |
| // Finalize all classes if needed. |
| Dart_Handle state = Api::CheckAndFinalizePendingClasses(I); |
| if (::Dart_IsError(state)) { |
| return state; |
| } |
| Library& lib = Library::Handle(Z); |
| if (library == Dart_Null()) { |
| lib ^= I->object_store()->root_library(); |
| } else { |
| lib ^= Api::UnwrapHandle(library); |
| } |
| I->heap()->CollectAllGarbage(); |
| #if defined(DEBUG) |
| FunctionVisitor check_canonical(T); |
| I->heap()->IterateObjects(&check_canonical); |
| #endif // #if defined(DEBUG). |
| ScriptSnapshotWriter writer(buffer, ApiReallocate); |
| writer.WriteScriptSnapshot(lib); |
| *size = writer.BytesWritten(); |
| return Api::Success(); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_CreateScriptSnapshot(uint8_t** buffer, |
| intptr_t* size) { |
| return createLibrarySnapshot(Dart_Null(), buffer, size); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_CreateLibrarySnapshot(Dart_Handle library, |
| uint8_t** buffer, |
| intptr_t* size) { |
| return createLibrarySnapshot(library, buffer, size); |
| } |
| |
| |
| DART_EXPORT void Dart_InterruptIsolate(Dart_Isolate isolate) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| if (isolate == NULL) { |
| FATAL1("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC); |
| } |
| // TODO(16615): Validate isolate parameter. |
| Isolate* iso = reinterpret_cast<Isolate*>(isolate); |
| iso->SendInternalLibMessage(Isolate::kInterruptMsg, iso->pause_capability()); |
| } |
| |
| |
| DART_EXPORT bool Dart_IsolateMakeRunnable(Dart_Isolate isolate) { |
| CHECK_NO_ISOLATE(Isolate::Current()); |
| if (isolate == NULL) { |
| FATAL1("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC); |
| } |
| // TODO(16615): Validate isolate parameter. |
| Isolate* iso = reinterpret_cast<Isolate*>(isolate); |
| if (iso->object_store()->root_library() == Library::null()) { |
| // The embedder should have called Dart_LoadScript by now. |
| return false; |
| } |
| return iso->MakeRunnable(); |
| } |
| |
| |
| // --- Messages and Ports --- |
| |
| DART_EXPORT void Dart_SetMessageNotifyCallback( |
| Dart_MessageNotifyCallback message_notify_callback) { |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| isolate->set_message_notify_callback(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() { |
| Thread* T = Thread::Current(); |
| Isolate* I = T->isolate(); |
| CHECK_ISOLATE_SCOPE(I); |
| CHECK_CALLBACK_STATE(T); |
| Monitor monitor; |
| MonitorLocker ml(&monitor); |
| { |
| SwitchIsolateScope switch_scope(NULL); |
| |
| RunLoopData data; |
| data.monitor = &monitor; |
| data.done = false; |
| I->message_handler()->Run( |
| Dart::thread_pool(), |
| NULL, RunLoopDone, reinterpret_cast<uword>(&data)); |
| while (!data.done) { |
| ml.Wait(); |
| } |
| } |
| if (I->object_store()->sticky_error() != Object::null()) { |
| Dart_Handle error = Api::NewHandle(I, I->object_store()->sticky_error()); |
| I->object_store()->clear_sticky_error(); |
| return error; |
| } |
| if (FLAG_print_class_table) { |
| HANDLESCOPE(T); |
| I->class_table()->Print(); |
| } |
| return Api::Success(); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_HandleMessage() { |
| Thread* thread = Thread::Current(); |
| Isolate* isolate = thread->isolate(); |
| CHECK_ISOLATE_SCOPE(isolate); |
| CHECK_CALLBACK_STATE(thread); |
| if (isolate->message_handler()->HandleNextMessage() != MessageHandler::kOK) { |
| Dart_Handle error = Api::NewHandle(isolate, |
| isolate->object_store()->sticky_error()); |
| isolate->object_store()->clear_sticky_error(); |
| return error; |
| } |
| return Api::Success(); |
| } |
| |
| |
| DART_EXPORT bool Dart_HandleServiceMessages() { |
| Thread* thread = Thread::Current(); |
| Isolate* isolate = thread->isolate(); |
| CHECK_ISOLATE_SCOPE(isolate); |
| CHECK_CALLBACK_STATE(thread); |
| |
| ASSERT(isolate->GetAndClearResumeRequest() == false); |
| MessageHandler::MessageStatus status = |
| isolate->message_handler()->HandleOOBMessages(); |
| bool resume = isolate->GetAndClearResumeRequest(); |
| return (status != MessageHandler::kOK) || resume; |
| } |
| |
| |
| DART_EXPORT bool Dart_HasServiceMessages() { |
| Isolate* isolate = Isolate::Current(); |
| ASSERT(isolate); |
| return isolate->message_handler()->HasOOBMessages(); |
| } |
| |
| |
| DART_EXPORT bool Dart_HasLivePorts() { |
| Isolate* isolate = Isolate::Current(); |
| ASSERT(isolate); |
| return isolate->message_handler()->HasLivePorts(); |
| } |
| |
| |
| static uint8_t* allocator(uint8_t* ptr, intptr_t old_size, intptr_t new_size) { |
| void* new_ptr = realloc(reinterpret_cast<void*>(ptr), new_size); |
| return reinterpret_cast<uint8_t*>(new_ptr); |
| } |
| |
| |
| DART_EXPORT bool Dart_Post(Dart_Port port_id, Dart_Handle handle) { |
| DARTSCOPE(Thread::Current()); |
| if (port_id == ILLEGAL_PORT) { |
| return false; |
| } |
| const Object& object = Object::Handle(Z, Api::UnwrapHandle(handle)); |
| uint8_t* data = NULL; |
| MessageWriter writer(&data, &allocator, false); |
| writer.WriteMessage(object); |
| intptr_t len = writer.BytesWritten(); |
| return PortMap::PostMessage(new Message( |
| port_id, data, len, 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(I, SendPort::New(port_id)); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_SendPortGetId(Dart_Handle port, |
| Dart_Port* port_id) { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(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); |
| ApiState* state = isolate->api_state(); |
| ASSERT(state != NULL); |
| ApiLocalScope* new_scope = state->reusable_scope(); |
| if (new_scope == NULL) { |
| new_scope = new ApiLocalScope(state->top_scope(), |
| thread->top_exit_frame_info()); |
| ASSERT(new_scope != NULL); |
| } else { |
| new_scope->Reinit(thread, |
| state->top_scope(), |
| thread->top_exit_frame_info()); |
| state->set_reusable_scope(NULL); |
| } |
| state->set_top_scope(new_scope); // New scope is now the top scope. |
| } |
| |
| |
| DART_EXPORT void Dart_ExitScope() { |
| Thread* thread = Thread::Current(); |
| Isolate* isolate = thread->isolate(); |
| CHECK_ISOLATE_SCOPE(isolate); |
| ApiState* state = isolate->api_state(); |
| ApiLocalScope* scope = state->top_scope(); |
| ApiLocalScope* reusable_scope = state->reusable_scope(); |
| state->set_top_scope(scope->previous()); // Reset top scope to previous. |
| if (reusable_scope == NULL) { |
| scope->Reset(thread); // Reset the old scope which we just exited. |
| state->set_reusable_scope(scope); |
| } else { |
| ASSERT(reusable_scope != scope); |
| delete scope; |
| } |
| } |
| |
| |
| DART_EXPORT uint8_t* Dart_ScopeAllocate(intptr_t size) { |
| Zone* zone; |
| Isolate* isolate = Isolate::Current(); |
| if (isolate != NULL) { |
| ApiState* state = isolate->api_state(); |
| if (state == NULL) return NULL; |
| ApiLocalScope* scope = state->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 Dart_Handle Dart_EmptyString() { |
| ASSERT(Isolate::Current() != NULL); |
| return Api::EmptyString(); |
| } |
| |
| |
| DART_EXPORT bool Dart_IsNull(Dart_Handle object) { |
| return Api::UnwrapHandle(object) == Object::null(); |
| } |
| |
| |
| 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(I, result.raw()); |
| } else { |
| return Api::NewError("Expected boolean result from =="); |
| } |
| } |
| |
| |
| // TODO(iposva): This call actually implements IsInstanceOfClass. |
| // Do we also need a real Dart_IsInstanceOf, which should take an instance |
| // rather than an object? |
| 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); |
| } |
| CHECK_CALLBACK_STATE(T); |
| Error& malformed_type_error = Error::Handle(Z); |
| *value = instance.IsInstanceOf(type_obj, |
| Object::null_type_arguments(), |
| &malformed_type_error); |
| ASSERT(malformed_type_error.IsNull()); // Type was created from a class. |
| return Api::Success(); |
| } |
| |
| |
| DART_EXPORT bool Dart_IsInstance(Dart_Handle object) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| REUSABLE_OBJECT_HANDLESCOPE(thread); |
| Object& ref = thread->ObjectHandle(); |
| ref = Api::UnwrapHandle(object); |
| return ref.IsInstance(); |
| } |
| |
| |
| DART_EXPORT bool Dart_IsNumber(Dart_Handle object) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return RawObject::IsNumberClassId(Api::ClassId(object)); |
| } |
| |
| |
| DART_EXPORT bool Dart_IsInteger(Dart_Handle object) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return RawObject::IsIntegerClassId(Api::ClassId(object)); |
| } |
| |
| |
| DART_EXPORT bool Dart_IsDouble(Dart_Handle object) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return Api::ClassId(object) == kDoubleCid; |
| } |
| |
| |
| DART_EXPORT bool Dart_IsBoolean(Dart_Handle object) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return Api::ClassId(object) == kBoolCid; |
| } |
| |
| |
| DART_EXPORT bool Dart_IsString(Dart_Handle object) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return RawObject::IsStringClassId(Api::ClassId(object)); |
| } |
| |
| |
| DART_EXPORT bool Dart_IsStringLatin1(Dart_Handle object) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return RawObject::IsOneByteStringClassId(Api::ClassId(object)); |
| } |
| |
| |
| DART_EXPORT bool Dart_IsExternalString(Dart_Handle object) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return RawObject::IsExternalStringClassId(Api::ClassId(object)); |
| } |
| |
| |
| DART_EXPORT bool Dart_IsList(Dart_Handle object) { |
| if (RawObject::IsBuiltinListClassId(Api::ClassId(object))) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return true; |
| } |
| |
| DARTSCOPE(Thread::Current()); |
| 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) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return Api::ClassId(object) == kLibraryCid; |
| } |
| |
| |
| DART_EXPORT bool Dart_IsType(Dart_Handle handle) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return Api::ClassId(handle) == kTypeCid; |
| } |
| |
| |
| DART_EXPORT bool Dart_IsFunction(Dart_Handle handle) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return Api::ClassId(handle) == kFunctionCid; |
| } |
| |
| |
| DART_EXPORT bool Dart_IsVariable(Dart_Handle handle) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return Api::ClassId(handle) == kFieldCid; |
| } |
| |
| |
| DART_EXPORT bool Dart_IsTypeVariable(Dart_Handle handle) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return Api::ClassId(handle) == kTypeParameterCid; |
| } |
| |
| |
| DART_EXPORT bool Dart_IsClosure(Dart_Handle object) { |
| // We can't use a fast class index check here because there are many |
| // different signature classes for closures. |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| ReusableObjectHandleScope reused_obj_handle(thread); |
| const Instance& closure_obj = |
| Api::UnwrapInstanceHandle(reused_obj_handle, object); |
| return (!closure_obj.IsNull() && closure_obj.IsClosure()); |
| } |
| |
| |
| DART_EXPORT bool Dart_IsTypedData(Dart_Handle handle) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| intptr_t cid = Api::ClassId(handle); |
| return RawObject::IsTypedDataClassId(cid) || |
| RawObject::IsExternalTypedDataClassId(cid) || |
| RawObject::IsTypedDataViewClassId(cid); |
| } |
| |
| |
| DART_EXPORT bool Dart_IsByteBuffer(Dart_Handle handle) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| return Api::ClassId(handle) == kByteBufferCid; |
| } |
| |
| |
| DART_EXPORT bool Dart_IsFuture(Dart_Handle handle) { |
| TRACE_API_CALL(CURRENT_FUNC); |
| DARTSCOPE(Thread::Current()); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(handle)); |
| if (obj.IsInstance()) { |
| const Class& future_class = |
| Class::Handle(I->object_store()->future_class()); |
| ASSERT(!future_class.IsNull()); |
| const Class& obj_class = Class::Handle(Z, obj.clazz()); |
| Error& malformed_type_error = Error::Handle(Z); |
| bool is_future = obj_class.IsSubtypeOf(Object::null_type_arguments(), |
| future_class, |
| Object::null_type_arguments(), |
| &malformed_type_error); |
| ASSERT(malformed_type_error.IsNull()); // Type is a raw Future. |
| return is_future; |
| } |
| return false; |
| } |
| |
| |
| // --- Instances ---- |
| |
| DART_EXPORT Dart_Handle Dart_InstanceGetType(Dart_Handle instance) { |
| DARTSCOPE(Thread::Current()); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(instance)); |
| if (obj.IsNull()) { |
| return Api::NewHandle(I, I->object_store()->null_type()); |
| } |
| if (!obj.IsInstance()) { |
| RETURN_TYPE_ERROR(Z, instance, Instance); |
| } |
| const Type& type = Type::Handle(Instance::Cast(obj).GetType()); |
| return Api::NewHandle(I, type.Canonicalize()); |
| } |
| |
| |
| // --- 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(); |
| Isolate* isolate = thread->isolate(); |
| CHECK_ISOLATE(isolate); |
| intptr_t class_id = Api::ClassId(integer); |
| if (class_id == kSmiCid || class_id == kMintCid) { |
| *fits = true; |
| return Api::Success(); |
| } |
| // Slow path for Mints and Bigints. |
| DARTSCOPE(thread); |
| const Integer& int_obj = Api::UnwrapIntegerHandle(Z, integer); |
| if (int_obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, integer, Integer); |
| } |
| ASSERT(!Bigint::Cast(int_obj).FitsIntoInt64()); |
| *fits = false; |
| return Api::Success(); |
| } |
| |
| |
| 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); |
| if (Api::IsSmi(integer)) { |
| *fits = (Api::SmiValue(integer) >= 0); |
| return Api::Success(); |
| } |
| // Slow path for Mints and Bigints. |
| DARTSCOPE(thread); |
| const Integer& int_obj = Api::UnwrapIntegerHandle(Z, integer); |
| if (int_obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, integer, Integer); |
| } |
| ASSERT(!int_obj.IsSmi()); |
| if (int_obj.IsMint()) { |
| *fits = !int_obj.IsNegative(); |
| } else { |
| *fits = Bigint::Cast(int_obj).FitsIntoUint64(); |
| } |
| 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); |
| if (Smi::IsValid(value)) { |
| NOHANDLESCOPE(thread); |
| return Api::NewHandle(isolate, Smi::New(static_cast<intptr_t>(value))); |
| } |
| // Slow path for Mints and Bigints. |
| DARTSCOPE(thread); |
| CHECK_CALLBACK_STATE(thread); |
| return Api::NewHandle(isolate, Integer::New(value)); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_NewIntegerFromUint64(uint64_t value) { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| return Api::NewHandle(I, Integer::NewFromUint64(value)); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_NewIntegerFromHexCString(const char* str) { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| const String& str_obj = String::Handle(Z, String::New(str)); |
| return Api::NewHandle(I, Integer::New(str_obj)); |
| } |
| |
| |
| 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 and Bigints. |
| DARTSCOPE(thread); |
| const Integer& int_obj = Api::UnwrapIntegerHandle(Z, integer); |
| if (int_obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, integer, Integer); |
| } |
| ASSERT(!int_obj.IsSmi()); |
| if (int_obj.IsMint()) { |
| *value = int_obj.AsInt64Value(); |
| return Api::Success(); |
| } else { |
| const Bigint& bigint = Bigint::Cast(int_obj); |
| if (bigint.FitsIntoInt64()) { |
| *value = bigint.AsInt64Value(); |
| return Api::Success(); |
| } |
| } |
| return Api::NewError("%s: Integer %s cannot be represented as an int64_t.", |
| CURRENT_FUNC, int_obj.ToCString()); |
| } |
| |
| |
| 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 and Bigints. |
| 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 if (int_obj.IsMint() && !int_obj.IsNegative()) { |
| *value = int_obj.AsInt64Value(); |
| return Api::Success(); |
| } else { |
| const Bigint& bigint = Bigint::Cast(int_obj); |
| if (bigint.FitsIntoUint64()) { |
| *value = bigint.AsUint64Value(); |
| return Api::Success(); |
| } |
| } |
| return Api::NewError("%s: Integer %s cannot be represented as a uint64_t.", |
| CURRENT_FUNC, int_obj.ToCString()); |
| } |
| |
| |
| static uword BigintAllocate(intptr_t size) { |
| return Api::TopScope(Isolate::Current())->zone()->AllocUnsafe(size); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_IntegerToHexCString(Dart_Handle integer, |
| const char** value) { |
| DARTSCOPE(Thread::Current()); |
| const Integer& int_obj = Api::UnwrapIntegerHandle(Z, integer); |
| if (int_obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, integer, Integer); |
| } |
| if (int_obj.IsSmi() || int_obj.IsMint()) { |
| const Bigint& bigint = Bigint::Handle(Z, |
| Bigint::NewFromInt64(int_obj.AsInt64Value())); |
| *value = bigint.ToHexCString(BigintAllocate); |
| } else { |
| *value = Bigint::Cast(int_obj).ToHexCString(BigintAllocate); |
| } |
| return Api::Success(); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_NewDouble(double value) { |
| DARTSCOPE(Thread::Current()); |
| CHECK_CALLBACK_STATE(T); |
| return Api::NewHandle(I, 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(); |
| } |
| |
| |
| // --- Booleans ---- |
| |
| DART_EXPORT Dart_Handle Dart_True() { |
| ASSERT(Isolate::Current() != NULL); |
| return Api::True(); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_False() { |
| ASSERT(Isolate::Current() != NULL); |
| return Api::False(); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_NewBoolean(bool value) { |
| Isolate* isolate = Isolate::Current(); |
| CHECK_ISOLATE(isolate); |
| return value ? Api::True() : Api::False(); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_BooleanValue(Dart_Handle boolean_obj, |
| bool* value) { |
| DARTSCOPE(Thread::Current()); |
| const Bool& obj = Api::UnwrapBoolHandle(Z, boolean_obj); |
| if (obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, boolean_obj, Bool); |
| } |
| *value = obj.value(); |
| return Api::Success(); |
| } |
| |
| |
| // --- Strings --- |
| |
| |
| DART_EXPORT Dart_Handle Dart_StringLength(Dart_Handle str, intptr_t* len) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| ReusableObjectHandleScope reused_obj_handle(thread); |
| const String& str_obj = Api::UnwrapStringHandle(reused_obj_handle, str); |
| if (str_obj.IsNull()) { |
| RETURN_TYPE_ERROR(thread->zone(), str, String); |
| } |
| *len = str_obj.Length(); |
| return Api::Success(); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_NewStringFromCString(const char* str) { |
| DARTSCOPE(Thread::Current()); |
| if (str == NULL) { |
| RETURN_NULL_ERROR(str); |
| } |
| CHECK_CALLBACK_STATE(T); |
| return Api::NewHandle(I, String::New(str)); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_NewStringFromUTF8(const uint8_t* utf8_array, |
| intptr_t length) { |
| DARTSCOPE(Thread::Current()); |
| if (utf8_array == NULL && length != 0) { |
| RETURN_NULL_ERROR(utf8_array); |
| } |
| CHECK_LENGTH(length, String::kMaxElements); |
| if (!Utf8::IsValid(utf8_array, length)) { |
| return Api::NewError("%s expects argument 'str' to be valid UTF-8.", |
| CURRENT_FUNC); |
| } |
| CHECK_CALLBACK_STATE(T); |
| return Api::NewHandle(I, String::FromUTF8(utf8_array, length)); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_NewStringFromUTF16(const uint16_t* utf16_array, |
| intptr_t length) { |
| DARTSCOPE(Thread::Current()); |
| if (utf16_array == NULL && length != 0) { |
| RETURN_NULL_ERROR(utf16_array); |
| } |
| CHECK_LENGTH(length, String::kMaxElements); |
| CHECK_CALLBACK_STATE(T); |
| return Api::NewHandle(I, String::FromUTF16(utf16_array, length)); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_NewStringFromUTF32(const int32_t* utf32_array, |
| intptr_t length) { |
| DARTSCOPE(Thread::Current()); |
| if (utf32_array == NULL && length != 0) { |
| RETURN_NULL_ERROR(utf32_array); |
| } |
| CHECK_LENGTH(length, String::kMaxElements); |
| CHECK_CALLBACK_STATE(T); |
| return Api::NewHandle(I, String::FromUTF32(utf32_array, length)); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_NewExternalLatin1String( |
| const uint8_t* latin1_array, |
| intptr_t length, |
| void* peer, |
| Dart_PeerFinalizer cback) { |
| DARTSCOPE(Thread::Current()); |
| if (latin1_array == NULL && length != 0) { |
| RETURN_NULL_ERROR(latin1_array); |
| } |
| CHECK_LENGTH(length, String::kMaxElements); |
| CHECK_CALLBACK_STATE(T); |
| return Api::NewHandle(I, |
| String::NewExternal(latin1_array, |
| length, |
| peer, |
| cback, |
| SpaceForExternal(I, length))); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_NewExternalUTF16String(const uint16_t* utf16_array, |
| intptr_t length, |
| void* peer, |
| Dart_PeerFinalizer cback) { |
| DARTSCOPE(Thread::Current()); |
| if (utf16_array == NULL && length != 0) { |
| RETURN_NULL_ERROR(utf16_array); |
| } |
| CHECK_LENGTH(length, String::kMaxElements); |
| CHECK_CALLBACK_STATE(T); |
| intptr_t bytes = length * sizeof(*utf16_array); |
| return Api::NewHandle(I, |
| String::NewExternal(utf16_array, |
| length, |
| peer, |
| cback, |
| SpaceForExternal(I, bytes))); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_StringToCString(Dart_Handle object, |
| const char** cstr) { |
| DARTSCOPE(Thread::Current()); |
| if (cstr == NULL) { |
| RETURN_NULL_ERROR(cstr); |
| } |
| const String& str_obj = Api::UnwrapStringHandle(Z, object); |
| if (str_obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, object, String); |
| } |
| intptr_t string_length = Utf8::Length(str_obj); |
| char* res = Api::TopScope(I)->zone()->Alloc<char>(string_length + 1); |
| if (res == NULL) { |
| return Api::NewError("Unable to allocate memory"); |
| } |
| const char* string_value = str_obj.ToCString(); |
| memmove(res, string_value, string_length + 1); |
| ASSERT(res[string_length] == '\0'); |
| *cstr = res; |
| return Api::Success(); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_StringToUTF8(Dart_Handle str, |
| uint8_t** utf8_array, |
| intptr_t* length) { |
| DARTSCOPE(Thread::Current()); |
| if (utf8_array == NULL) { |
| RETURN_NULL_ERROR(utf8_array); |
| } |
| if (length == NULL) { |
| RETURN_NULL_ERROR(length); |
| } |
| const String& str_obj = Api::UnwrapStringHandle(Z, str); |
| if (str_obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, str, String); |
| } |
| intptr_t str_len = Utf8::Length(str_obj); |
| *utf8_array = Api::TopScope(I)->zone()->Alloc<uint8_t>(str_len); |
| if (*utf8_array == NULL) { |
| return Api::NewError("Unable to allocate memory"); |
| } |
| str_obj.ToUTF8(*utf8_array, str_len); |
| *length = str_len; |
| return Api::Success(); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_StringToLatin1(Dart_Handle str, |
| uint8_t* latin1_array, |
| intptr_t* length) { |
| DARTSCOPE(Thread::Current()); |
| if (latin1_array == NULL) { |
| RETURN_NULL_ERROR(latin1_array); |
| } |
| if (length == NULL) { |
| RETURN_NULL_ERROR(length); |
| } |
| const String& str_obj = Api::UnwrapStringHandle(Z, str); |
| if (str_obj.IsNull() || !str_obj.IsOneByteString()) { |
| RETURN_TYPE_ERROR(Z, str, String); |
| } |
| intptr_t str_len = str_obj.Length(); |
| intptr_t copy_len = (str_len > *length) ? *length : str_len; |
| |
| // We have already asserted that the string object is a Latin-1 string |
| // so we can copy the characters over using a simple loop. |
| for (intptr_t i = 0; i < copy_len; i++) { |
| latin1_array[i] = str_obj.CharAt(i); |
| } |
| *length = copy_len; |
| return Api::Success(); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_StringToUTF16(Dart_Handle str, |
| uint16_t* utf16_array, |
| intptr_t* length) { |
| DARTSCOPE(Thread::Current()); |
| const String& str_obj = Api::UnwrapStringHandle(Z, str); |
| if (str_obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, str, String); |
| } |
| intptr_t str_len = str_obj.Length(); |
| intptr_t copy_len = (str_len > *length) ? *length : str_len; |
| for (intptr_t i = 0; i < copy_len; i++) { |
| utf16_array[i] = str_obj.CharAt(i); |
| } |
| *length = copy_len; |
| return Api::Success(); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_StringStorageSize(Dart_Handle str, |
| intptr_t* size) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| ReusableObjectHandleScope reused_obj_handle(thread); |
| const String& str_obj = Api::UnwrapStringHandle(reused_obj_handle, str); |
| if (str_obj.IsNull()) { |
| RETURN_TYPE_ERROR(thread->zone(), str, String); |
| } |
| if (size == NULL) { |
| RETURN_NULL_ERROR(size); |
| } |
| *size = (str_obj.Length() * str_obj.CharSize()); |
| return Api::Success(); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_MakeExternalString(Dart_Handle str, |
| void* array, |
| intptr_t length, |
| void* peer, |
| Dart_PeerFinalizer cback) { |
| DARTSCOPE(Thread::Current()); |
| const String& str_obj = Api::UnwrapStringHandle(Z, str); |
| if (str_obj.IsExternal()) { |
| return str; // String is already an external string. |
| } |
| if (str_obj.IsNull()) { |
| RETURN_TYPE_ERROR(Z, str, String); |
| } |
| if (array == NULL) { |
| RETURN_NULL_ERROR(array); |
| } |
| intptr_t str_size = (str_obj.Length() * str_obj.CharSize()); |
| if ((length < str_size) || (length > String::kMaxElements)) { |
| return Api::NewError("Dart_MakeExternalString " |
| "expects argument length to be in the range" |
| "[%" Pd "..%" Pd "].", |
| str_size, String::kMaxElements); |
| } |
| if (str_obj.InVMHeap()) { |
| // Since the string object is read only we do not externalize |
| // the string but instead copy the contents of the string into the |
| // specified buffer add the specified peer/cback as a Peer object |
| // to this string. The Api::StringGetPeerHelper function picks up |
| // the peer from the Peer table. |
| intptr_t copy_len = str_obj.Length(); |
| if (str_obj.IsOneByteString()) { |
| ASSERT(length >= copy_len); |
| uint8_t* latin1_array = reinterpret_cast<uint8_t*>(array); |
| for (intptr_t i = 0; i < copy_len; i++) { |
| latin1_array[i] = static_cast<uint8_t>(str_obj.CharAt(i)); |
| } |
| OneByteString::SetPeer(str_obj, peer, cback); |
| } else { |
| ASSERT(str_obj.IsTwoByteString()); |
| ASSERT(length >= (copy_len * str_obj.CharSize())); |
| uint16_t* utf16_array = reinterpret_cast<uint16_t*>(array); |
| for (intptr_t i = 0; i < copy_len; i++) { |
| utf16_array[i] = str_obj.CharAt(i); |
| } |
| TwoByteString::SetPeer(str_obj, peer, cback); |
| } |
| return str; |
| } |
| return Api::NewHandle(I, str_obj.MakeExternal(array, length, peer, cback)); |
| } |
| |
| |
| DART_EXPORT Dart_Handle Dart_StringGetProperties(Dart_Handle object, |
| intptr_t* char_size, |
| intptr_t* str_len, |
| void** peer) { |
| Thread* thread = Thread::Current(); |
| CHECK_ISOLATE(thread->isolate()); |
| ReusableObjectHandleScope reused_obj_handle(thread); |
| const String& str = Api::UnwrapStringHandle(reused_obj_handle, object); |
| if (str.IsNull()) { |
| RETURN_TYPE_ERROR(thread->zone(), object, String); |
| } |
| if (str.IsExternal()) { |
| *peer = str.GetPeer(); |
| ASSERT(*peer != NULL); |
| } else { |
| NoSafepointScope no_safepoint_scope; |
| *peer = thread->isolate()->heap()->GetPeer(str.raw()); |
| } |
| *char_size = str.CharSize(); |
| *str_len = str.Length(); |
| return Api::Success(); |
| } |
| |
| |
| // --- Lists --- |
| |
| DART_EXPORT Dart_Handle Dart_NewList(intptr_t length) { |
| DARTSCOPE(Thread::Current()); |
| CHECK_LENGTH(length, Array::kMaxElements); |
| CHECK_CALLBACK_STATE(T); |
| return Api::NewHandle(I, Array::New(length)); |
| } |
| |
| |
| #define GET_LIST_LENGTH(zone, type, obj, len) \ |
| type& array = type::Handle(zone); \ |
| array ^= obj.raw(); \ |
| *len = array.Length(); \ |
| return Api::Success(); \ |
| |
| |
| DART_EXPORT Dart_Handle Dart_ListLength(Dart_Handle list, intptr_t* len) { |
| DARTSCOPE(Thread::Current()); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(list)); |
| if (obj.IsError()) { |
| // Pass through errors. |
| return list; |
| } |
| if (obj.IsTypedData()) { |
| GET_LIST_LENGTH(Z, TypedData, obj, len); |
| } |
| if (obj.IsArray()) { |
| GET_LIST_LENGTH(Z, Array, obj, len); |
| } |
| if (obj.IsGrowableObjectArray()) { |
| GET_LIST_LENGTH(Z, GrowableObjectArray, obj, len); |
| } |
| if (obj.IsExternalTypedData()) { |
| GET_LIST_LENGTH(Z, ExternalTypedData, obj, len); |
| } |
| CHECK_CALLBACK_STATE(T); |
| |
| // Now check and handle a dart object that implements the List interface. |
| const Instance& instance = Instance::Handle(Z, GetListInstance(Z, obj)); |
| if (instance.IsNull()) { |
| return Api::NewError("Object does not implement the List interface"); |
| } |
| const String& name = String::Handle(Z, Field::GetterName(Symbols::Length())); |
| const int kNumArgs = 1; |
| ArgumentsDescriptor args_desc( |
| Array::Handle(Z, ArgumentsDescriptor::New(kNumArgs))); |
| const Function& function = |
| Function::Handle(Z, Resolver::ResolveDynamic(instance, name, args_desc)); |
| if (function.IsNull()) { |
| return Api::NewError("List object does not have a 'length' field."); |
| } |
| |
| const Array& args = Array::Handle(Z, Array::New(kNumArgs)); |
| args.SetAt(0, instance); // Set up the receiver as the first argument. |
| const Object& retval = |
| Object::Handle(Z, DartEntry::InvokeFunction(function, args)); |
| if (retval.IsSmi()) { |
| *len = Smi::Cast(retval).Value(); |
| return Api::Success(); |
| } else if (retval.IsMint() || retval.IsBigint()) { |
| if (retval.IsMint()) { |
| int64_t mint_value = Mint::Cast(retval).value(); |
| if (mint_value >= kIntptrMin && mint_value <= kIntptrMax) { |
| *len = static_cast<intptr_t>(mint_value); |
| } |
| } else { |
| // Check for a non-canonical Mint range value. |
| ASSERT(retval.IsBigint()); |
| const Bigint& bigint = Bigint::Handle(); |
| if (bigint.FitsIntoInt64()) { |
| int64_t bigint_value = bigint.AsInt64Value(); |
| if (bigint_value >= kIntptrMin && bigint_value <= kIntptrMax) { |
| *len = static_cast<intptr_t>(bigint_value); |
| } |
| } |
| } |
| return Api::NewError("Length of List object is greater than the " |
| "maximum value that 'len' parameter can hold"); |
| } else if (retval.IsError()) { |
| return Api::NewHandle(I, retval.raw()); |
| } else { |
| return Api::NewError("Length of List object is not an integer"); |
| } |
| } |
| |
| |
| #define GET_LIST_ELEMENT(isolate, type, obj, index) \ |
| const type& array_obj = type::Cast(obj); \ |
| if ((index >= 0) && (index < array_obj.Length())) { \ |
| return Api::NewHandle(isolate, array_obj.At(index)); \ |
| } \ |
| return Api::NewError("Invalid index passed in to access list element"); \ |
| |
| |
| DART_EXPORT Dart_Handle Dart_ListGetAt(Dart_Handle list, intptr_t index) { |
| DARTSCOPE(Thread::Current()); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(list)); |
| if (obj.IsArray()) { |
| GET_LIST_ELEMENT(I, Array, obj, index); |
| } else if (obj.IsGrowableObjectArray()) { |
| GET_LIST_ELEMENT(I, GrowableObjectArray, obj, index); |
| } else if (obj.IsError()) { |
| return list; |
| } else { |
| CHECK_CALLBACK_STATE(T); |
| // Check and handle a dart object that implements the List interface. |
| const Instance& instance = Instance::Handle(Z, GetListInstance(Z, obj)); |
| if (!instance.IsNull()) { |
| return Api::NewHandle(I, Send1Arg( |
| instance, |
| Symbols::IndexToken(), |
| Instance::Handle(Z, Integer::New(index)))); |
| } |
| return Api::NewError("Object does not implement the 'List' interface"); |
| } |
| } |
| |
| |
| #define GET_LIST_RANGE(isolate, type, obj, offset, length) \ |
| const type& array_obj = type::Cast(obj); \ |
| if ((offset >= 0) && (offset + length <= array_obj.Length())) { \ |
| for (intptr_t index = 0; index < length; ++index) { \ |
| result[index] = Api::NewHandle(isolate, array_obj.At(index + offset)); \ |
| } \ |
| return Api::Success(); \ |
| } \ |
| return Api::NewError("Invalid offset/length passed in to access list"); \ |
| |
| |
| DART_EXPORT Dart_Handle Dart_ListGetRange(Dart_Handle list, |
| intptr_t offset, |
| intptr_t length, |
| Dart_Handle* result) { |
| DARTSCOPE(Thread::Current()); |
| if (result == NULL) { |
| RETURN_NULL_ERROR(result); |
| } |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(list)); |
| if (obj.IsArray()) { |
| GET_LIST_RANGE(I, Array, obj, offset, length); |
| } else if (obj.IsGrowableObjectArray()) { |
| GET_LIST_RANGE(I, GrowableObjectArray, obj, offset, length); |
| } else if (obj.IsError()) { |
| return list; |
| } else { |
| CHECK_CALLBACK_STATE(T); |
| // Check and handle a dart object that implements the List interface. |
| const Instance& instance = Instance::Handle(Z, GetListInstance(Z, obj)); |
| if (!instance.IsNull()) { |
| const intptr_t kNumArgs = 2; |
| ArgumentsDescriptor args_desc( |
| Array::Handle(ArgumentsDescriptor::New(kNumArgs))); |
| const Function& function = Function::Handle(Z, |
| Resolver::ResolveDynamic(instance, |
| Symbols::AssignIndexToken(), |
| args_desc)); |
| if (!function.IsNull()) { |
| const Array& args = Array::Handle(Array::New(kNumArgs)); |
| args.SetAt(0, instance); |
| Instance& index = Instance::Handle(Z); |
| for (intptr_t i = 0; i < length; ++i) { |
| index = Integer::New(i); |
| args.SetAt(1, index); |
| Dart_Handle value = Api::NewHandle(I, |
| DartEntry::InvokeFunction(function, args)); |
| if (::Dart_IsError(value)) |
| return value; |
| result[i] = value; |
| } |
| return Api::Success(); |
| } |
| } |
| return Api::NewError("Object does not implement the 'List' interface"); |
| } |
| } |
| |
| |
| #define SET_LIST_ELEMENT(type, obj, index, value) \ |
| const type& array = type::Cast(obj); \ |
| const Object& value_obj = Object::Handle(Z, Api::UnwrapHandle(value)); \ |
| if (!value_obj.IsNull() && !value_obj.IsInstance()) { \ |
| RETURN_TYPE_ERROR(Z, value, Instance); \ |
| } \ |
| if ((index >= 0) && (index < array.Length())) { \ |
| array.SetAt(index, value_obj); \ |
| return Api::Success(); \ |
| } \ |
| return Api::NewError("Invalid index passed in to set list element"); \ |
| |
| |
| DART_EXPORT Dart_Handle Dart_ListSetAt(Dart_Handle list, |
| intptr_t index, |
| Dart_Handle value) { |
| DARTSCOPE(Thread::Current()); |
| const Object& obj = Object::Handle(Z, Api::UnwrapHandle(list)); |
| // If the list is immutable we call into Dart for the indexed setter to |
| // get the unsupported operation exception as the result. |
| if (obj.IsArray() && !Array::Cast(obj).IsImmutable()) { |
| SET_LIST_ELEMENT(Array, obj, index, value); |
| } else if (obj.IsGrowab
|