// Copyright (c) 2012, 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 "vm/bigint_operations.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/debuginfo.h" #include "vm/exceptions.h" #include "vm/flags.h" #include "vm/growable_array.h" #include "vm/message.h" #include "vm/native_entry.h" #include "vm/native_message_handler.h" #include "vm/object.h" #include "vm/object_store.h" #include "vm/port.h" #include "vm/resolver.h" #include "vm/stack_frame.h" #include "vm/symbols.h" #include "vm/timer.h" #include "vm/unicode.h" #include "vm/verifier.h" #include "vm/version.h" namespace dart { DECLARE_FLAG(bool, print_class_table); ThreadLocalKey Api::api_native_key_ = Thread::kUnsetThreadLocalKey; const char* CanonicalFunction(const char* func) { if (strncmp(func, "dart::", 6) == 0) { return func + 6; } else { return func; } } #define RETURN_TYPE_ERROR(isolate, dart_handle, type) \ do { \ const Object& tmp = \ Object::Handle(isolate, Api::UnwrapHandle((dart_handle))); \ if (tmp.IsNull()) { \ return Api::NewError("%s expects argument '%s' to be non-null.", \ CURRENT_FUNC, #dart_handle); \ } else if (tmp.IsError()) { \ return dart_handle; \ } else { \ return Api::NewError("%s expects argument '%s' to be of type %s.", \ CURRENT_FUNC, #dart_handle, #type); \ } \ } while (0) #define RETURN_NULL_ERROR(parameter) \ return Api::NewError("%s expects argument '%s' to be non-null.", \ CURRENT_FUNC, #parameter); #define CHECK_LENGTH(length, max_elements) \ do { \ intptr_t len = (length); \ intptr_t max = (max_elements); \ if (len < 0 || len > max) { \ return Api::NewError( \ "%s expects argument '%s' to be in the range [0..%"Pd"].", \ CURRENT_FUNC, #length, max); \ } \ } while (0) void SetupErrorResult(Isolate* isolate, Dart_Handle* handle) { *handle = Api::NewHandle( isolate, Isolate::Current()->object_store()->sticky_error()); } Dart_Handle Api::NewHandle(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 reinterpret_cast(ref); } 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(state->IsValidLocalHandle(object) || state->IsValidPersistentHandle(object) || state->IsValidWeakPersistentHandle(object) || state->IsValidPrologueWeakPersistentHandle(object)); ASSERT(FinalizablePersistentHandle::raw_offset() == 0 && PersistentHandle::raw_offset() == 0 && LocalHandle::raw_offset() == 0); #endif return (reinterpret_cast(object))->raw(); } #define DEFINE_UNWRAP(type) \ const type& Api::Unwrap##type##Handle(Isolate* iso, \ Dart_Handle dart_handle) { \ const Object& obj = Object::Handle(iso, Api::UnwrapHandle(dart_handle)); \ if (obj.Is##type()) { \ return type::Cast(obj); \ } \ return type::Handle(iso); \ } CLASS_LIST_FOR_HANDLES(DEFINE_UNWRAP) #undef DEFINE_UNWRAP LocalHandle* Api::UnwrapAsLocalHandle(const ApiState& state, Dart_Handle object) { ASSERT(state.IsValidLocalHandle(object)); return reinterpret_cast(object); } PersistentHandle* Api::UnwrapAsPersistentHandle(const ApiState& state, Dart_Handle object) { ASSERT(state.IsValidPersistentHandle(object)); return reinterpret_cast(object); } FinalizablePersistentHandle* Api::UnwrapAsWeakPersistentHandle( const ApiState& state, Dart_Handle object) { ASSERT(state.IsValidWeakPersistentHandle(object)); return reinterpret_cast(object); } FinalizablePersistentHandle* Api::UnwrapAsPrologueWeakPersistentHandle( const ApiState& state, Dart_Handle object) { ASSERT(state.IsValidPrologueWeakPersistentHandle(object)); return reinterpret_cast(object); } Dart_Handle Api::CheckIsolateState(Isolate* isolate) { if (ClassFinalizer::FinalizePendingClasses() && isolate->object_store()->PreallocateObjects()) { return Api::Success(isolate); } const Object& obj = Object::Handle(isolate->object_store()->sticky_error()); ASSERT(obj.IsError()); return Api::NewHandle(isolate, obj.raw()); } Dart_Isolate Api::CastIsolate(Isolate* isolate) { return reinterpret_cast(isolate); } Dart_Handle Api::Success(Isolate* isolate) { ASSERT(isolate != NULL); ApiState* state = isolate->api_state(); ASSERT(state != NULL); PersistentHandle* true_handle = state->True(); return reinterpret_cast(true_handle); } Dart_Handle Api::NewError(const char* format, ...) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); va_list args; va_start(args, format); intptr_t len = OS::VSNPrint(NULL, 0, format, args); va_end(args); char* buffer = isolate->current_zone()->Alloc(len + 1); va_list args2; va_start(args2, format); OS::VSNPrint(buffer, (len + 1), format, args2); va_end(args2); const String& message = String::Handle(isolate, String::New(buffer)); return Api::NewHandle(isolate, ApiError::New(message)); } void Api::SetupCallbackError(Isolate* isolate) { ASSERT(isolate != NULL); ApiState* state = isolate->api_state(); ASSERT(state != NULL); state->SetupCallbackError(); } Dart_Handle Api::CallbackError(Isolate* isolate) { ASSERT(isolate != NULL); ApiState* state = isolate->api_state(); ASSERT(state != NULL); PersistentHandle* callback_error_handle = state->CallbackError(); return reinterpret_cast(callback_error_handle); } Dart_Handle Api::Null(Isolate* isolate) { ASSERT(isolate != NULL); ApiState* state = isolate->api_state(); ASSERT(state != NULL); PersistentHandle* null_handle = state->Null(); return reinterpret_cast(null_handle); } Dart_Handle Api::True(Isolate* isolate) { ASSERT(isolate != NULL); ApiState* state = isolate->api_state(); ASSERT(state != NULL); PersistentHandle* true_handle = state->True(); return reinterpret_cast(true_handle); } Dart_Handle Api::False(Isolate* isolate) { ASSERT(isolate != NULL); ApiState* state = isolate->api_state(); ASSERT(state != NULL); PersistentHandle* false_handle = state->False(); return reinterpret_cast(false_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_ == Thread::kUnsetThreadLocalKey); api_native_key_ = Thread::CreateThreadLocal(); ASSERT(api_native_key_ != Thread::kUnsetThreadLocalKey); } bool Api::ExternalStringGetPeerHelper(Dart_Handle object, void** peer) { NoGCScope no_gc_scope; RawObject* raw_obj = Api::UnwrapHandle(object); switch (Api::ClassId(object)) { case kExternalOneByteStringCid: { RawExternalOneByteString* raw_string = reinterpret_cast(raw_obj)->ptr(); ExternalStringData* data = raw_string->external_data_; *peer = data->peer(); return true; } case kExternalTwoByteStringCid: { RawExternalTwoByteString* raw_string = reinterpret_cast(raw_obj)->ptr(); ExternalStringData* data = raw_string->external_data_; *peer = data->peer(); return true; } } return false; } // When we want to return a handle to a type to the user, we handle // class-types differently than some other types. static Dart_Handle TypeToHandle(Isolate* isolate, const char* function_name, const AbstractType& type) { if (type.IsMalformed()) { const Error& error = Error::Handle(type.malformed_error()); return Api::NewError("%s: malformed type encountered: %s.", function_name, error.ToErrorCString()); } else if (type.HasResolvedTypeClass()) { const Class& cls = Class::Handle(isolate, type.type_class()); #if defined(DEBUG) const Library& lib = Library::Handle(cls.library()); if (lib.IsNull()) { ASSERT(cls.IsDynamicClass() || cls.IsVoidClass()); } #endif return Api::NewHandle(isolate, cls.raw()); } else if (type.IsTypeParameter()) { return Api::NewHandle(isolate, type.raw()); } else { return Api::NewError("%s: unexpected type '%s' encountered.", function_name, type.ToCString()); } } // --- Handles --- DART_EXPORT bool Dart_IsError(Dart_Handle handle) { return RawObject::IsErrorClassId(Api::ClassId(handle)); } DART_EXPORT bool Dart_IsApiError(Dart_Handle object) { return Api::ClassId(object) == kApiErrorCid; } DART_EXPORT bool Dart_IsUnhandledExceptionError(Dart_Handle object) { return Api::ClassId(object) == kUnhandledExceptionCid; } DART_EXPORT bool Dart_IsCompilationError(Dart_Handle object) { return Api::ClassId(object) == kLanguageErrorCid; } DART_EXPORT bool Dart_IsFatalError(Dart_Handle object) { return Api::ClassId(object) == kUnwindErrorCid; } DART_EXPORT const char* Dart_GetError(Dart_Handle handle) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, 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(isolate)->zone()->Alloc(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) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(handle)); return obj.IsUnhandledException(); } DART_EXPORT Dart_Handle Dart_ErrorGetException(Dart_Handle handle) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(handle)); if (obj.IsUnhandledException()) { const UnhandledException& error = UnhandledException::Cast(obj); return Api::NewHandle(isolate, 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) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(handle)); if (obj.IsUnhandledException()) { const UnhandledException& error = UnhandledException::Cast(obj); return Api::NewHandle(isolate, 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."); } } // Deprecated. // TODO(turnidge): Remove all uses and delete. DART_EXPORT Dart_Handle Dart_Error(const char* format, ...) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); va_list args; va_start(args, format); intptr_t len = OS::VSNPrint(NULL, 0, format, args); va_end(args); char* buffer = isolate->current_zone()->Alloc(len + 1); va_list args2; va_start(args2, format); OS::VSNPrint(buffer, (len + 1), format, args2); va_end(args2); const String& message = String::Handle(isolate, String::New(buffer)); return Api::NewHandle(isolate, ApiError::New(message)); } // 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* format, ...) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); va_list args; va_start(args, format); intptr_t len = OS::VSNPrint(NULL, 0, format, args); va_end(args); char* buffer = isolate->current_zone()->Alloc(len + 1); va_list args2; va_start(args2, format); OS::VSNPrint(buffer, (len + 1), format, args2); va_end(args2); const String& message = String::Handle(isolate, String::New(buffer)); return Api::NewHandle(isolate, ApiError::New(message)); } DART_EXPORT Dart_Handle Dart_NewUnhandledExceptionError(Dart_Handle exception) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); const Instance& obj = Api::UnwrapInstanceHandle(isolate, exception); if (obj.IsNull()) { RETURN_TYPE_ERROR(isolate, exception, Instance); } const Instance& stacktrace = Instance::Handle(isolate); return Api::NewHandle(isolate, UnhandledException::New(obj, stacktrace)); } DART_EXPORT Dart_Handle Dart_PropagateError(Dart_Handle handle) { Isolate* isolate = Isolate::Current(); { const Object& obj = Object::Handle(isolate, 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 NoGCScope, we can ensure // that GC won't touch the raw error object before creating a valid // handle for it in the surviving zone. NoGCScope no_gc; RawError* raw_error = Api::UnwrapErrorHandle(isolate, handle).raw(); state->UnwindScopes(isolate->top_exit_frame_info()); error = &Error::Handle(isolate, 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) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(object)); if (obj.IsString()) { return Api::NewHandle(isolate, obj.raw()); } else if (obj.IsInstance()) { CHECK_CALLBACK_STATE(isolate); const Instance& receiver = Instance::Cast(obj); return Api::NewHandle(isolate, DartLibraryCalls::ToString(receiver)); } else { CHECK_CALLBACK_STATE(isolate); // This is a VM internal object. Call the C++ method of printing. return Api::NewHandle(isolate, String::New(obj.ToCString())); } } DART_EXPORT bool Dart_IdentityEquals(Dart_Handle obj1, Dart_Handle obj2) { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); NoGCScope ngc; return Api::UnwrapHandle(obj1) == Api::UnwrapHandle(obj2); } DART_EXPORT Dart_Handle Dart_NewPersistentHandle(Dart_Handle object) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); ApiState* state = isolate->api_state(); ASSERT(state != NULL); const Object& old_ref = Object::Handle(isolate, Api::UnwrapHandle(object)); PersistentHandle* new_ref = state->persistent_handles().AllocateHandle(); new_ref->set_raw(old_ref); return reinterpret_cast(new_ref); } static Dart_Handle AllocateFinalizableHandle( Isolate* isolate, FinalizablePersistentHandles* handles, Dart_Handle object, void* peer, Dart_WeakPersistentHandleFinalizer callback) { const Object& ref = Object::Handle(isolate, Api::UnwrapHandle(object)); FinalizablePersistentHandle* finalizable_ref = handles->AllocateHandle(); finalizable_ref->set_raw(ref); finalizable_ref->set_peer(peer); finalizable_ref->set_callback(callback); return reinterpret_cast(finalizable_ref); } DART_EXPORT Dart_Handle Dart_NewWeakPersistentHandle( Dart_Handle object, void* peer, Dart_WeakPersistentHandleFinalizer callback) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); ApiState* state = isolate->api_state(); ASSERT(state != NULL); return AllocateFinalizableHandle(isolate, &state->weak_persistent_handles(), object, peer, callback); } DART_EXPORT Dart_Handle Dart_NewPrologueWeakPersistentHandle( Dart_Handle object, void* peer, Dart_WeakPersistentHandleFinalizer callback) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); ApiState* state = isolate->api_state(); ASSERT(state != NULL); return AllocateFinalizableHandle(isolate, &state->prologue_weak_persistent_handles(), object, peer, callback); } DART_EXPORT void Dart_DeletePersistentHandle(Dart_Handle object) { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); ApiState* state = isolate->api_state(); ASSERT(state != NULL); if (state->IsValidPrologueWeakPersistentHandle(object)) { FinalizablePersistentHandle* prologue_weak_ref = Api::UnwrapAsPrologueWeakPersistentHandle(*state, object); state->prologue_weak_persistent_handles().FreeHandle(prologue_weak_ref); return; } if (state->IsValidWeakPersistentHandle(object)) { FinalizablePersistentHandle* weak_ref = Api::UnwrapAsWeakPersistentHandle(*state, object); state->weak_persistent_handles().FreeHandle(weak_ref); return; } PersistentHandle* ref = Api::UnwrapAsPersistentHandle(*state, object); ASSERT(!state->IsProtectedHandle(ref)); if (!state->IsProtectedHandle(ref)) { state->persistent_handles().FreeHandle(ref); } } DART_EXPORT bool Dart_IsWeakPersistentHandle(Dart_Handle object) { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); ApiState* state = isolate->api_state(); ASSERT(state != NULL); return state->IsValidWeakPersistentHandle(object); } DART_EXPORT bool Dart_IsPrologueWeakPersistentHandle(Dart_Handle object) { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); ApiState* state = isolate->api_state(); ASSERT(state != NULL); return state->IsValidPrologueWeakPersistentHandle(object); } DART_EXPORT Dart_Handle Dart_NewWeakReferenceSet(Dart_Handle* keys, intptr_t num_keys, Dart_Handle* values, intptr_t num_values) { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); ApiState* state = isolate->api_state(); ASSERT(state != NULL); if (keys == NULL) { RETURN_NULL_ERROR(keys); } if (num_keys <= 0) { return Api::NewError( "%s expects argument 'num_keys' to be greater than 0.", CURRENT_FUNC); } if (values == NULL) { RETURN_NULL_ERROR(values); } if (num_values <= 0) { return Api::NewError( "%s expects argument 'num_values' to be greater than 0.", CURRENT_FUNC); } WeakReferenceSet* reference_set = new WeakReferenceSet(keys, num_keys, values, num_values); state->DelayWeakReferenceSet(reference_set); return Api::Success(isolate); } // --- Garbage Collection Callbacks -- DART_EXPORT Dart_Handle Dart_AddGcPrologueCallback( Dart_GcPrologueCallback callback) { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); GcPrologueCallbacks& callbacks = isolate->gc_prologue_callbacks(); if (callbacks.Contains(callback)) { return Api::NewError( "%s permits only one instance of 'callback' to be present in the " "prologue callback list.", CURRENT_FUNC); } callbacks.Add(callback); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_RemoveGcPrologueCallback( Dart_GcPrologueCallback callback) { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); GcPrologueCallbacks& callbacks = isolate->gc_prologue_callbacks(); if (!callbacks.Contains(callback)) { return Api::NewError( "%s expects 'callback' to be present in the prologue callback list.", CURRENT_FUNC); } callbacks.Remove(callback); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_AddGcEpilogueCallback( Dart_GcEpilogueCallback callback) { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); GcEpilogueCallbacks& callbacks = isolate->gc_epilogue_callbacks(); if (callbacks.Contains(callback)) { return Api::NewError( "%s permits only one instance of 'callback' to be present in the " "epilogue callback list.", CURRENT_FUNC); } callbacks.Add(callback); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_RemoveGcEpilogueCallback( Dart_GcEpilogueCallback callback) { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); GcEpilogueCallbacks& callbacks = isolate->gc_epilogue_callbacks(); if (!callbacks.Contains(callback)) { return Api::NewError( "%s expects 'callback' to be present in the epilogue callback list.", CURRENT_FUNC); } callbacks.Remove(callback); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_HeapProfile(Dart_FileWriteCallback callback, void* stream) { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); if (callback == NULL) { RETURN_NULL_ERROR(callback); } isolate->heap()->Profile(callback, stream); return Api::Success(isolate); } // --- Initialization and Globals --- DART_EXPORT const char* Dart_VersionString() { return Version::String(); } DART_EXPORT bool Dart_Initialize( Dart_IsolateCreateCallback create, Dart_IsolateInterruptCallback interrupt, Dart_IsolateUnhandledExceptionCallback unhandled, Dart_IsolateShutdownCallback shutdown, Dart_FileOpenCallback file_open, Dart_FileWriteCallback file_write, Dart_FileCloseCallback file_close) { const char* err_msg = Dart::InitOnce(create, interrupt, unhandled, shutdown, file_open, file_write, file_close); if (err_msg != NULL) { OS::PrintErr("Dart_Initialize: %s\n", err_msg); return false; } return true; } DART_EXPORT bool Dart_SetVMFlags(int argc, const char** argv) { return Flags::ProcessCommandLineFlags(argc, argv); } DART_EXPORT bool Dart_IsVMFlagSet(const char* flag_name) { if (Flags::Lookup(flag_name) != NULL) { return true; } return false; } // --- 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); } } // 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(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, void* callback_data, char** error) { char* isolate_name = BuildIsolateName(script_uri, main); Isolate* isolate = Dart::CreateIsolate(isolate_name); free(isolate_name); { StackZone zone(isolate); HANDLESCOPE(isolate); const Error& error_obj = Error::Handle(isolate, Dart::InitializeIsolate(snapshot, callback_data)); if (error_obj.IsNull()) { START_TIMER(time_total_runtime); return reinterpret_cast(isolate); } *error = strdup(error_obj.ToErrorCString()); } Dart::ShutdownIsolate(); return reinterpret_cast(NULL); } DART_EXPORT void Dart_ShutdownIsolate() { CHECK_ISOLATE(Isolate::Current()); STOP_TIMER(time_total_runtime); 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 Dart_Handle Dart_DebugName() { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); return Api::NewHandle(isolate, String::New(isolate->name())); } DART_EXPORT void Dart_EnterIsolate(Dart_Isolate dart_isolate) { CHECK_NO_ISOLATE(Isolate::Current()); Isolate* isolate = reinterpret_cast(dart_isolate); Isolate::SetCurrent(isolate); } DART_EXPORT void Dart_ExitIsolate() { CHECK_ISOLATE(Isolate::Current()); Isolate::SetCurrent(NULL); } static uint8_t* ApiReallocate(uint8_t* ptr, intptr_t old_size, intptr_t new_size) { return Api::TopScope(Isolate::Current())->zone()->Realloc( ptr, old_size, new_size); } DART_EXPORT Dart_Handle Dart_CreateSnapshot(uint8_t** buffer, intptr_t* size) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); TIMERSCOPE(time_creating_snapshot); if (buffer == NULL) { RETURN_NULL_ERROR(buffer); } if (size == NULL) { RETURN_NULL_ERROR(size); } Dart_Handle state = Api::CheckIsolateState(isolate); if (::Dart_IsError(state)) { return state; } // Since this is only a snapshot the root library should not be set. isolate->object_store()->set_root_library(Library::Handle(isolate)); FullSnapshotWriter writer(buffer, ApiReallocate); writer.WriteFullSnapshot(); *size = writer.BytesWritten(); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_CreateScriptSnapshot(uint8_t** buffer, intptr_t* size) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); TIMERSCOPE(time_creating_snapshot); if (buffer == NULL) { RETURN_NULL_ERROR(buffer); } if (size == NULL) { RETURN_NULL_ERROR(size); } Dart_Handle state = Api::CheckIsolateState(isolate); if (::Dart_IsError(state)) { return state; } Library& library = Library::Handle(isolate, isolate->object_store()->root_library()); if (library.IsNull()) { return Api::NewError("%s expects the isolate to have a script loaded in it.", CURRENT_FUNC); } ScriptSnapshotWriter writer(buffer, ApiReallocate); writer.WriteScriptSnapshot(library); *size = writer.BytesWritten(); return Api::Success(isolate); } DART_EXPORT void Dart_InterruptIsolate(Dart_Isolate isolate) { if (isolate == NULL) { FATAL1("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC); } Isolate* iso = reinterpret_cast(isolate); iso->ScheduleInterrupts(Isolate::kApiInterrupt); } // --- 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(param); ASSERT(data->monitor != NULL); MonitorLocker ml(data->monitor); data->done = true; ml.Notify(); } DART_EXPORT Dart_Handle Dart_RunLoop() { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); Monitor monitor; MonitorLocker ml(&monitor); { SwitchIsolateScope switch_scope(NULL); RunLoopData data; data.monitor = &monitor; data.done = false; isolate->message_handler()->Run( Dart::thread_pool(), NULL, RunLoopDone, reinterpret_cast(&data)); while (!data.done) { ml.Wait(); } } const Object& obj = Object::Handle(isolate->object_store()->sticky_error()); isolate->object_store()->clear_sticky_error(); if (obj.IsError()) { return Api::NewHandle(isolate, obj.raw()); } ASSERT(obj.IsNull()); if (FLAG_print_class_table) { isolate->class_table()->Print(); } return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_HandleMessage() { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); if (!isolate->message_handler()->HandleNextMessage()) { // TODO(turnidge): Clear sticky error here? return Api::NewHandle(isolate, isolate->object_store()->sticky_error()); } return Api::Success(isolate); } 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(ptr), new_size); return reinterpret_cast(new_ptr); } DART_EXPORT bool Dart_PostIntArray(Dart_Port port_id, intptr_t len, intptr_t* data) { uint8_t* buffer = NULL; ApiMessageWriter writer(&buffer, &allocator); writer.WriteMessage(len, data); // Post the message at the given port. return PortMap::PostMessage(new Message( port_id, Message::kIllegalPort, buffer, writer.BytesWritten(), Message::kNormalPriority)); } DART_EXPORT bool Dart_PostCObject(Dart_Port port_id, Dart_CObject* message) { uint8_t* buffer = NULL; ApiMessageWriter writer(&buffer, allocator); bool success = writer.WriteCMessage(message); if (!success) return success; // Post the message at the given port. return PortMap::PostMessage(new Message( port_id, Message::kIllegalPort, buffer, writer.BytesWritten(), Message::kNormalPriority)); } DART_EXPORT bool Dart_Post(Dart_Port port_id, Dart_Handle handle) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& object = Object::Handle(isolate, Api::UnwrapHandle(handle)); uint8_t* data = NULL; MessageWriter writer(&data, &allocator); writer.WriteMessage(object); intptr_t len = writer.BytesWritten(); return PortMap::PostMessage(new Message( port_id, Message::kIllegalPort, data, len, Message::kNormalPriority)); } DART_EXPORT Dart_Port Dart_NewNativePort(const char* name, Dart_NativeMessageHandler handler, bool handle_concurrently) { if (name == NULL) { name = ""; } if (handler == NULL) { OS::PrintErr("%s expects argument 'handler' to be non-null.\n", CURRENT_FUNC); return ILLEGAL_PORT; } // Start the native port without a current isolate. IsolateSaver saver(Isolate::Current()); Isolate::SetCurrent(NULL); NativeMessageHandler* nmh = new NativeMessageHandler(name, handler); Dart_Port port_id = PortMap::CreatePort(nmh); nmh->Run(Dart::thread_pool(), NULL, NULL, 0); return port_id; } DART_EXPORT bool Dart_CloseNativePort(Dart_Port native_port_id) { // Close the native port without a current isolate. IsolateSaver saver(Isolate::Current()); Isolate::SetCurrent(NULL); // TODO(turnidge): Check that the port is native before trying to close. return PortMap::ClosePort(native_port_id); } DART_EXPORT Dart_Handle Dart_NewSendPort(Dart_Port port_id) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); return Api::NewHandle(isolate, DartLibraryCalls::NewSendPort(port_id)); } DART_EXPORT Dart_Handle Dart_GetReceivePort(Dart_Port port_id) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); Library& isolate_lib = Library::Handle(isolate, Library::IsolateLibrary()); ASSERT(!isolate_lib.IsNull()); const String& class_name = String::Handle( isolate, isolate_lib.PrivateName(Symbols::_ReceivePortImpl())); // TODO(asiva): Symbols should contain private keys. const String& function_name = String::Handle(isolate_lib.PrivateName(Symbols::_get_or_create())); const int kNumArguments = 1; const Function& function = Function::Handle( isolate, Resolver::ResolveStatic(isolate_lib, class_name, function_name, kNumArguments, Object::empty_array(), Resolver::kIsQualified)); ASSERT(!function.IsNull()); const Array& args = Array::Handle(isolate, Array::New(kNumArguments)); args.SetAt(0, Integer::Handle(isolate, Integer::New(port_id))); return Api::NewHandle(isolate, DartEntry::InvokeStatic(function, args)); } DART_EXPORT Dart_Port Dart_GetMainPortId() { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); return isolate->main_port(); } // --- Scopes ---- DART_EXPORT void Dart_EnterScope() { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); ApiState* state = isolate->api_state(); ASSERT(state != NULL); ApiLocalScope* new_scope = new ApiLocalScope(state->top_scope(), isolate->top_exit_frame_info()); ASSERT(new_scope != NULL); state->set_top_scope(new_scope); // New scope is now the top scope. } DART_EXPORT void Dart_ExitScope() { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE_SCOPE(isolate); ApiState* state = isolate->api_state(); ApiLocalScope* scope = state->top_scope(); state->set_top_scope(scope->previous()); // Reset top scope to previous. delete scope; // Free up the old scope which we have just exited. } 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(zone->AllocUnsafe(size)); } // --- Objects ---- DART_EXPORT Dart_Handle Dart_Null() { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE_SCOPE(isolate); return Api::Null(isolate); } DART_EXPORT bool Dart_IsNull(Dart_Handle object) { return Api::ClassId(object) == kNullCid; } DART_EXPORT Dart_Handle Dart_ObjectEquals(Dart_Handle obj1, Dart_Handle obj2, bool* value) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); const Instance& expected = Instance::CheckedHandle(isolate, Api::UnwrapHandle(obj1)); const Instance& actual = Instance::CheckedHandle(isolate, Api::UnwrapHandle(obj2)); const Object& result = Object::Handle(isolate, DartLibraryCalls::Equals(expected, actual)); if (result.IsBool()) { *value = Bool::Cast(result).value(); return Api::Success(isolate); } else if (result.IsError()) { return Api::NewHandle(isolate, 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 and a type rather than a class? DART_EXPORT Dart_Handle Dart_ObjectIsType(Dart_Handle object, Dart_Handle clazz, bool* value) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Class& cls = Api::UnwrapClassHandle(isolate, clazz); if (cls.IsNull()) { RETURN_TYPE_ERROR(isolate, clazz, Class); } const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(object)); if (obj.IsError()) { return object; } else if (!obj.IsNull() && !obj.IsInstance()) { return Api::NewError( "%s expects argument 'object' to be an instance of Object.", CURRENT_FUNC); } // Finalize all classes. Dart_Handle state = Api::CheckIsolateState(isolate); if (::Dart_IsError(state)) { return state; } if (obj.IsInstance()) { CHECK_CALLBACK_STATE(isolate); const Type& type = Type::Handle(isolate, Type::NewNonParameterizedType(cls)); Error& malformed_type_error = Error::Handle(isolate); *value = Instance::Cast(obj).IsInstanceOf(type, TypeArguments::Handle(isolate), &malformed_type_error); ASSERT(malformed_type_error.IsNull()); // Type was created from a class. } else { *value = false; } return Api::Success(isolate); } // --- Instances ---- DART_EXPORT bool Dart_IsInstance(Dart_Handle object) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(object)); return obj.IsInstance(); } // TODO(turnidge): Technically, null has a class. Should we allow it? DART_EXPORT Dart_Handle Dart_InstanceGetClass(Dart_Handle instance) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Instance& obj = Api::UnwrapInstanceHandle(isolate, instance); if (obj.IsNull()) { RETURN_TYPE_ERROR(isolate, instance, Instance); } return Api::NewHandle(isolate, obj.clazz()); } // --- Numbers ---- DART_EXPORT bool Dart_IsNumber(Dart_Handle object) { return RawObject::IsNumberClassId(Api::ClassId(object)); } // --- Integers ---- DART_EXPORT bool Dart_IsInteger(Dart_Handle object) { return RawObject::IsIntegerClassId(Api::ClassId(object)); } DART_EXPORT Dart_Handle Dart_IntegerFitsIntoInt64(Dart_Handle integer, bool* fits) { // Fast path for Smis and Mints. Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); intptr_t class_id = Api::ClassId(integer); if (class_id == kSmiCid || class_id == kMintCid) { *fits = true; return Api::Success(isolate); } // Slow path for Mints and Bigints. DARTSCOPE(isolate); const Integer& int_obj = Api::UnwrapIntegerHandle(isolate, integer); if (int_obj.IsNull()) { RETURN_TYPE_ERROR(isolate, integer, Integer); } ASSERT(!BigintOperations::FitsIntoMint(Bigint::Cast(int_obj))); *fits = false; return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_IntegerFitsIntoUint64(Dart_Handle integer, bool* fits) { // Fast path for Smis. Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); if (Api::IsSmi(integer)) { *fits = (Api::SmiValue(integer) >= 0); return Api::Success(isolate); } // Slow path for Mints and Bigints. DARTSCOPE(isolate); const Integer& int_obj = Api::UnwrapIntegerHandle(isolate, integer); if (int_obj.IsNull()) { RETURN_TYPE_ERROR(isolate, integer, Integer); } ASSERT(!int_obj.IsSmi()); if (int_obj.IsMint()) { *fits = !int_obj.IsNegative(); } else { *fits = BigintOperations::FitsIntoUint64(Bigint::Cast(int_obj)); } return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_NewInteger(int64_t value) { // Fast path for Smis. Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); if (Smi::IsValid64(value)) { NOHANDLESCOPE(isolate); return Api::NewHandle(isolate, Smi::New(static_cast(value))); } // Slow path for Mints and Bigints. DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); return Api::NewHandle(isolate, Integer::New(value)); } DART_EXPORT Dart_Handle Dart_NewIntegerFromHexCString(const char* str) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); const String& str_obj = String::Handle(isolate, String::New(str)); return Api::NewHandle(isolate, Integer::New(str_obj)); } DART_EXPORT Dart_Handle Dart_IntegerToInt64(Dart_Handle integer, int64_t* value) { // Fast path for Smis. Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); if (Api::IsSmi(integer)) { *value = Api::SmiValue(integer); return Api::Success(isolate); } // Slow path for Mints and Bigints. DARTSCOPE(isolate); const Integer& int_obj = Api::UnwrapIntegerHandle(isolate, integer); if (int_obj.IsNull()) { RETURN_TYPE_ERROR(isolate, integer, Integer); } ASSERT(!int_obj.IsSmi()); if (int_obj.IsMint()) { *value = int_obj.AsInt64Value(); return Api::Success(isolate); } else { const Bigint& bigint = Bigint::Cast(int_obj); if (BigintOperations::FitsIntoMint(bigint)) { *value = BigintOperations::ToMint(bigint); return Api::Success(isolate); } } 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. Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); if (Api::IsSmi(integer)) { intptr_t smi_value = Api::SmiValue(integer); if (smi_value >= 0) { *value = smi_value; return Api::Success(isolate); } } // Slow path for Mints and Bigints. DARTSCOPE(isolate); const Integer& int_obj = Api::UnwrapIntegerHandle(isolate, integer); if (int_obj.IsNull()) { RETURN_TYPE_ERROR(isolate, integer, Integer); } ASSERT(!int_obj.IsSmi()); if (int_obj.IsMint() && !int_obj.IsNegative()) { *value = int_obj.AsInt64Value(); return Api::Success(isolate); } else { const Bigint& bigint = Bigint::Cast(int_obj); if (BigintOperations::FitsIntoUint64(bigint)) { *value = BigintOperations::ToUint64(bigint); return Api::Success(isolate); } } 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) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Integer& int_obj = Api::UnwrapIntegerHandle(isolate, integer); if (int_obj.IsNull()) { RETURN_TYPE_ERROR(isolate, integer, Integer); } if (int_obj.IsSmi() || int_obj.IsMint()) { const Bigint& bigint = Bigint::Handle(isolate, BigintOperations::NewFromInt64(int_obj.AsInt64Value())); *value = BigintOperations::ToHexCString(bigint, BigintAllocate); } else { *value = BigintOperations::ToHexCString(Bigint::Cast(int_obj), BigintAllocate); } return Api::Success(isolate); } // --- Booleans ---- DART_EXPORT Dart_Handle Dart_True() { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE_SCOPE(isolate); return Api::True(isolate); } DART_EXPORT Dart_Handle Dart_False() { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE_SCOPE(isolate); return Api::False(isolate); } DART_EXPORT bool Dart_IsBoolean(Dart_Handle object) { return Api::ClassId(object) == kBoolCid; } DART_EXPORT Dart_Handle Dart_NewBoolean(bool value) { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE_SCOPE(isolate); return value ? Api::True(isolate) : Api::False(isolate); } DART_EXPORT Dart_Handle Dart_BooleanValue(Dart_Handle boolean_obj, bool* value) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Bool& obj = Api::UnwrapBoolHandle(isolate, boolean_obj); if (obj.IsNull()) { RETURN_TYPE_ERROR(isolate, boolean_obj, Bool); } *value = obj.value(); return Api::Success(isolate); } // --- Doubles --- DART_EXPORT bool Dart_IsDouble(Dart_Handle object) { return Api::ClassId(object) == kDoubleCid; } DART_EXPORT Dart_Handle Dart_NewDouble(double value) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); return Api::NewHandle(isolate, Double::New(value)); } DART_EXPORT Dart_Handle Dart_DoubleValue(Dart_Handle double_obj, double* value) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Double& obj = Api::UnwrapDoubleHandle(isolate, double_obj); if (obj.IsNull()) { RETURN_TYPE_ERROR(isolate, double_obj, Double); } *value = obj.value(); return Api::Success(isolate); } // --- Strings --- DART_EXPORT bool Dart_IsString(Dart_Handle object) { return RawObject::IsStringClassId(Api::ClassId(object)); } DART_EXPORT bool Dart_IsStringLatin1(Dart_Handle object) { return RawObject::IsOneByteStringClassId(Api::ClassId(object)); } DART_EXPORT Dart_Handle Dart_StringLength(Dart_Handle str, intptr_t* len) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const String& str_obj = Api::UnwrapStringHandle(isolate, str); if (str_obj.IsNull()) { RETURN_TYPE_ERROR(isolate, str, String); } *len = str_obj.Length(); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_NewStringFromCString(const char* str) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (str == NULL) { RETURN_NULL_ERROR(str); } CHECK_CALLBACK_STATE(isolate); return Api::NewHandle(isolate, String::New(str)); } DART_EXPORT Dart_Handle Dart_NewStringFromUTF8(const uint8_t* utf8_array, intptr_t length) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); 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(isolate); return Api::NewHandle(isolate, String::FromUTF8(utf8_array, length)); } DART_EXPORT Dart_Handle Dart_NewStringFromUTF16(const uint16_t* utf16_array, intptr_t length) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (utf16_array == NULL && length != 0) { RETURN_NULL_ERROR(utf16_array); } CHECK_LENGTH(length, String::kMaxElements); CHECK_CALLBACK_STATE(isolate); return Api::NewHandle(isolate, String::FromUTF16(utf16_array, length)); } DART_EXPORT Dart_Handle Dart_NewStringFromUTF32(const int32_t* utf32_array, intptr_t length) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (utf32_array == NULL && length != 0) { RETURN_NULL_ERROR(utf32_array); } CHECK_LENGTH(length, String::kMaxElements); CHECK_CALLBACK_STATE(isolate); return Api::NewHandle(isolate, String::FromUTF32(utf32_array, length)); } DART_EXPORT bool Dart_IsExternalString(Dart_Handle object) { return RawObject::IsExternalStringClassId(Api::ClassId(object)); } DART_EXPORT Dart_Handle Dart_ExternalStringGetPeer(Dart_Handle object, void** peer) { if (peer == NULL) { RETURN_NULL_ERROR(peer); } if (Api::ExternalStringGetPeerHelper(object, peer)) { return Api::Success(Isolate::Current()); } // It's not an external string, return appropriate error. if (!RawObject::IsStringClassId(Api::ClassId(object))) { RETURN_TYPE_ERROR(Isolate::Current(), object, String); } else { return Api::NewError( "%s expects argument 'object' to be an external String.", CURRENT_FUNC); } } DART_EXPORT Dart_Handle Dart_NewExternalLatin1String( const uint8_t* latin1_array, intptr_t length, void* peer, Dart_PeerFinalizer cback) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (latin1_array == NULL && length != 0) { RETURN_NULL_ERROR(latin1_array); } CHECK_LENGTH(length, String::kMaxElements); CHECK_CALLBACK_STATE(isolate); return Api::NewHandle(isolate, String::NewExternal(latin1_array, length, peer, cback)); } DART_EXPORT Dart_Handle Dart_NewExternalUTF16String(const uint16_t* utf16_array, intptr_t length, void* peer, Dart_PeerFinalizer cback) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (utf16_array == NULL && length != 0) { RETURN_NULL_ERROR(utf16_array); } CHECK_LENGTH(length, String::kMaxElements); CHECK_CALLBACK_STATE(isolate); return Api::NewHandle(isolate, String::NewExternal(utf16_array, length, peer, cback)); } DART_EXPORT Dart_Handle Dart_StringToCString(Dart_Handle object, const char** cstr) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (cstr == NULL) { RETURN_NULL_ERROR(cstr); } const String& str_obj = Api::UnwrapStringHandle(isolate, object); if (str_obj.IsNull()) { RETURN_TYPE_ERROR(isolate, object, String); } intptr_t string_length = Utf8::Length(str_obj); char* res = Api::TopScope(isolate)->zone()->Alloc(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(isolate); } DART_EXPORT Dart_Handle Dart_StringToUTF8(Dart_Handle str, uint8_t** utf8_array, intptr_t* length) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (utf8_array == NULL) { RETURN_NULL_ERROR(utf8_array); } if (length == NULL) { RETURN_NULL_ERROR(length); } const String& str_obj = Api::UnwrapStringHandle(isolate, str); if (str_obj.IsNull()) { RETURN_TYPE_ERROR(isolate, str, String); } intptr_t str_len = Utf8::Length(str_obj); *utf8_array = Api::TopScope(isolate)->zone()->Alloc(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(isolate); } DART_EXPORT Dart_Handle Dart_StringToLatin1(Dart_Handle str, uint8_t* latin1_array, intptr_t* length) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (latin1_array == NULL) { RETURN_NULL_ERROR(latin1_array); } if (length == NULL) { RETURN_NULL_ERROR(length); } const String& str_obj = Api::UnwrapStringHandle(isolate, str); if (str_obj.IsNull() || !str_obj.IsOneByteString()) { RETURN_TYPE_ERROR(isolate, 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(isolate); } DART_EXPORT Dart_Handle Dart_StringToUTF16(Dart_Handle str, uint16_t* utf16_array, intptr_t* length) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const String& str_obj = Api::UnwrapStringHandle(isolate, str); if (str_obj.IsNull()) { RETURN_TYPE_ERROR(isolate, 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] = static_cast(str_obj.CharAt(i)); } *length = copy_len; return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_StringStorageSize(Dart_Handle str, intptr_t* size) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const String& str_obj = Api::UnwrapStringHandle(isolate, str); if (str_obj.IsNull()) { RETURN_TYPE_ERROR(isolate, str, String); } if (size == NULL) { RETURN_NULL_ERROR(size); } *size = (str_obj.Length() * str_obj.CharSize()); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_MakeExternalString(Dart_Handle str, void* array, intptr_t length, void* peer, Dart_PeerFinalizer cback) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const String& str_obj = Api::UnwrapStringHandle(isolate, str); if (str_obj.IsExternal()) { return str; // String is already an external string. } if (str_obj.IsNull()) { RETURN_TYPE_ERROR(isolate, 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 and return a Null object. // This ensures that the embedder does not have to call again // to get at the contents. intptr_t copy_len = str_obj.Length(); if (str_obj.IsOneByteString()) { ASSERT(length >= copy_len); uint8_t* latin1_array = reinterpret_cast(array); for (intptr_t i = 0; i < copy_len; i++) { latin1_array[i] = static_cast(str_obj.CharAt(i)); } } else { ASSERT(str_obj.IsTwoByteString()); ASSERT(length >= (copy_len * str_obj.CharSize())); uint16_t* utf16_array = reinterpret_cast(array); for (intptr_t i = 0; i < copy_len; i++) { utf16_array[i] = static_cast(str_obj.CharAt(i)); } } return Api::Null(isolate); } return Api::NewHandle(isolate, str_obj.MakeExternal(array, length, peer, cback)); } // --- Lists --- static RawInstance* GetListInstance(Isolate* isolate, const Object& obj) { if (obj.IsInstance()) { const Instance& instance = Instance::Cast(obj); const Class& obj_class = Class::Handle(isolate, obj.clazz()); const Class& list_class = Class::Handle(isolate, isolate->object_store()->list_class()); Error& malformed_type_error = Error::Handle(isolate); if (obj_class.IsSubtypeOf(TypeArguments::Handle(isolate), list_class, TypeArguments::Handle(isolate), &malformed_type_error)) { ASSERT(malformed_type_error.IsNull()); // Type is a raw List. return instance.raw(); } } return Instance::null(); } DART_EXPORT bool Dart_IsList(Dart_Handle object) { if (RawObject::IsBuiltinListClassId(Api::ClassId(object))) { return true; } Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(object)); return GetListInstance(isolate, obj) != Instance::null(); } DART_EXPORT Dart_Handle Dart_NewList(intptr_t length) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_LENGTH(length, Array::kMaxElements); CHECK_CALLBACK_STATE(isolate); return Api::NewHandle(isolate, Array::New(length)); } #define GET_LIST_LENGTH(isolate, type, obj, len) \ type& array = type::Handle(isolate); \ array ^= obj.raw(); \ *len = array.Length(); \ return Api::Success(isolate); \ DART_EXPORT Dart_Handle Dart_ListLength(Dart_Handle list, intptr_t* len) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(list)); if (obj.IsError()) { // Pass through errors. return list; } if (obj.IsByteArray()) { GET_LIST_LENGTH(isolate, ByteArray, obj, len); } if (obj.IsArray()) { GET_LIST_LENGTH(isolate, Array, obj, len); } if (obj.IsGrowableObjectArray()) { GET_LIST_LENGTH(isolate, GrowableObjectArray, obj, len); } CHECK_CALLBACK_STATE(isolate); // Now check and handle a dart object that implements the List interface. const Instance& instance = Instance::Handle(isolate, GetListInstance(isolate, obj)); if (instance.IsNull()) { return Api::NewError("Object does not implement the List interface"); } const String& name = String::Handle(Field::GetterName(Symbols::Length())); const Function& function = Function::Handle(isolate, Resolver::ResolveDynamic(instance, name, 1, 0)); if (function.IsNull()) { return Api::NewError("List object does not have a 'length' field."); } const int kNumArgs = 1; const Array& args = Array::Handle(isolate, Array::New(kNumArgs)); args.SetAt(0, instance); // Set up the receiver as the first argument. const Object& retval = Object::Handle(isolate, DartEntry::InvokeDynamic(function, args)); if (retval.IsSmi()) { *len = Smi::Cast(retval).Value(); return Api::Success(isolate); } 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(mint_value); } } else { // Check for a non-canonical Mint range value. ASSERT(retval.IsBigint()); const Bigint& bigint = Bigint::Handle(); if (BigintOperations::FitsIntoMint(bigint)) { int64_t bigint_value = bigint.AsInt64Value(); if (bigint_value >= kIntptrMin && bigint_value <= kIntptrMax) { *len = static_cast(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(isolate, 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) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(list)); if (obj.IsArray()) { GET_LIST_ELEMENT(isolate, Array, obj, index); } else if (obj.IsGrowableObjectArray()) { GET_LIST_ELEMENT(isolate, GrowableObjectArray, obj, index); } else if (obj.IsError()) { return list; } else { CHECK_CALLBACK_STATE(isolate); // Check and handle a dart object that implements the List interface. const Instance& instance = Instance::Handle(isolate, GetListInstance(isolate, obj)); if (!instance.IsNull()) { const Function& function = Function::Handle( isolate, Resolver::ResolveDynamic(instance, Symbols::IndexToken(), 2, 0)); if (!function.IsNull()) { const int kNumArgs = 2; const Array& args = Array::Handle(isolate, Array::New(kNumArgs)); const Integer& indexobj = Integer::Handle(isolate, Integer::New(index)); args.SetAt(0, instance); args.SetAt(1, indexobj); return Api::NewHandle(isolate, DartEntry::InvokeDynamic(function, args)); } } return Api::NewError("Object does not implement the 'List' interface"); } } #define SET_LIST_ELEMENT(isolate, type, obj, index, value) \ const type& array = type::Cast(obj); \ const Object& value_obj = Object::Handle(isolate, Api::UnwrapHandle(value)); \ if (!value_obj.IsNull() && !value_obj.IsInstance()) { \ RETURN_TYPE_ERROR(isolate, value, Instance); \ } \ if ((index >= 0) && (index < array.Length())) { \ array.SetAt(index, value_obj); \ return Api::Success(isolate); \ } \ 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) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, 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() && !obj.IsImmutableArray()) { SET_LIST_ELEMENT(isolate, Array, obj, index, value); } else if (obj.IsGrowableObjectArray()) { SET_LIST_ELEMENT(isolate, GrowableObjectArray, obj, index, value); } else if (obj.IsError()) { return list; } else { CHECK_CALLBACK_STATE(isolate); // Check and handle a dart object that implements the List interface. const Instance& instance = Instance::Handle(isolate, GetListInstance(isolate, obj)); if (!instance.IsNull()) { const Function& function = Function::Handle( isolate, Resolver::ResolveDynamic(instance, Symbols::AssignIndexToken(), 3, 0)); if (!function.IsNull()) { const Integer& index_obj = Integer::Handle(isolate, Integer::New(index)); const Object& value_obj = Object::Handle(isolate, Api::UnwrapHandle(value)); if (!value_obj.IsNull() && !value_obj.IsInstance()) { RETURN_TYPE_ERROR(isolate, value, Instance); } const intptr_t kNumArgs = 3; const Array& args = Array::Handle(isolate, Array::New(kNumArgs)); args.SetAt(0, instance); args.SetAt(1, index_obj); args.SetAt(2, value_obj); return Api::NewHandle(isolate, DartEntry::InvokeDynamic(function, args)); } } return Api::NewError("Object does not implement the 'List' interface"); } } // TODO(hpayer): value should always be smaller then 0xff. Add error handling. #define GET_LIST_ELEMENT_AS_BYTES(isolate, type, obj, native_array, offset, \ length) \ const type& array = type::Cast(obj); \ if (Utils::RangeCheck(offset, length, array.Length())) { \ Object& element = Object::Handle(isolate); \ for (int i = 0; i < length; i++) { \ element = array.At(offset + i); \ if (!element.IsInteger()) { \ return Api::NewError("%s expects the argument 'list' to be " \ "a List of int", CURRENT_FUNC); \ } \ const Integer& integer = Integer::Cast(element); \ native_array[i] = static_cast(integer.AsInt64Value() & 0xff); \ ASSERT(integer.AsInt64Value() <= 0xff); \ } \ return Api::Success(isolate); \ } \ return Api::NewError("Invalid length passed in to access array elements"); \ DART_EXPORT Dart_Handle Dart_ListGetAsBytes(Dart_Handle list, intptr_t offset, uint8_t* native_array, intptr_t length) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(list)); if (obj.IsUint8Array() || obj.IsExternalUint8Array() || obj.IsUint8ClampedArray() || obj.IsExternalUint8ClampedArray() || obj.IsInt8Array() || obj.IsExternalInt8Array()) { const ByteArray& byte_array = ByteArray::Cast(obj); if (Utils::RangeCheck(offset, length, byte_array.Length())) { ByteArray::Copy(native_array, byte_array, offset, length); return Api::Success(isolate); } return Api::NewError("Invalid length passed in to access list elements"); } else if (obj.IsArray()) { GET_LIST_ELEMENT_AS_BYTES(isolate, Array, obj, native_array, offset, length); } else if (obj.IsGrowableObjectArray()) { GET_LIST_ELEMENT_AS_BYTES(isolate, GrowableObjectArray, obj, native_array, offset, length); } else if (obj.IsError()) { return list; } else { CHECK_CALLBACK_STATE(isolate); // Check and handle a dart object that implements the List interface. const Instance& instance = Instance::Handle(isolate, GetListInstance(isolate, obj)); if (!instance.IsNull()) { const Function& function = Function::Handle( isolate, Resolver::ResolveDynamic(instance, Symbols::IndexToken(), 2, 0)); if (!function.IsNull()) { Object& result = Object::Handle(isolate); Integer& intobj = Integer::Handle(isolate); const int kNumArgs = 2; const Array& args = Array::Handle(isolate, Array::New(kNumArgs)); args.SetAt(0, instance); // Set up the receiver as the first argument. for (int i = 0; i < length; i++) { intobj = Integer::New(offset + i); args.SetAt(1, intobj); result = DartEntry::InvokeDynamic(function, args); if (result.IsError()) { return Api::NewHandle(isolate, result.raw()); } if (!result.IsInteger()) { return Api::NewError("%s expects the argument 'list' to be " "a List of int", CURRENT_FUNC); } const Integer& integer_result = Integer::Cast(result); ASSERT(integer_result.AsInt64Value() <= 0xff); // TODO(hpayer): value should always be smaller then 0xff. Add error // handling. native_array[i] = static_cast(integer_result.AsInt64Value() & 0xff); } return Api::Success(isolate); } } return Api::NewError("Object does not implement the 'List' interface"); } } #define SET_LIST_ELEMENT_AS_BYTES(isolate, type, obj, native_array, offset, \ length) \ const type& array = type::Cast(obj); \ Integer& integer = Integer::Handle(isolate); \ if (Utils::RangeCheck(offset, length, array.Length())) { \ for (int i = 0; i < length; i++) { \ integer = Integer::New(native_array[i]); \ array.SetAt(offset + i, integer); \ } \ return Api::Success(isolate); \ } \ return Api::NewError("Invalid length passed in to set array elements"); \ DART_EXPORT Dart_Handle Dart_ListSetAsBytes(Dart_Handle list, intptr_t offset, uint8_t* native_array, intptr_t length) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(list)); if (obj.IsUint8Array() || obj.IsExternalUint8Array() || obj.IsUint8ClampedArray() || obj.IsExternalUint8ClampedArray()) { const ByteArray& byte_array = ByteArray::Cast(obj); if (Utils::RangeCheck(offset, length, byte_array.Length())) { ByteArray::Copy(byte_array, offset, native_array, length); return Api::Success(isolate); } return Api::NewError("Invalid length passed in to set list elements"); } else if (obj.IsArray() && !obj.IsImmutableArray()) { // If the list is immutable we call into Dart for the indexed setter to // get the unsupported operation exception as the result. SET_LIST_ELEMENT_AS_BYTES(isolate, Array, obj, native_array, offset, length); } else if (obj.IsGrowableObjectArray()) { SET_LIST_ELEMENT_AS_BYTES(isolate, GrowableObjectArray, obj, native_array, offset, length); } else if (obj.IsError()) { return list; } else { CHECK_CALLBACK_STATE(isolate); // Check and handle a dart object that implements the List interface. const Instance& instance = Instance::Handle(isolate, GetListInstance(isolate, obj)); if (!instance.IsNull()) { const Function& function = Function::Handle( isolate, Resolver::ResolveDynamic(instance, Symbols::AssignIndexToken(), 3, 0)); if (!function.IsNull()) { Integer& indexobj = Integer::Handle(isolate); Integer& valueobj = Integer::Handle(isolate); const int kNumArgs = 3; const Array& args = Array::Handle(isolate, Array::New(kNumArgs)); args.SetAt(0, instance); // Set up the receiver as the first argument. for (int i = 0; i < length; i++) { indexobj = Integer::New(offset + i); valueobj = Integer::New(native_array[i]); args.SetAt(1, indexobj); args.SetAt(2, valueobj); const Object& result = Object::Handle( isolate, DartEntry::InvokeDynamic(function, args)); if (result.IsError()) { return Api::NewHandle(isolate, result.raw()); } } return Api::Success(isolate); } } return Api::NewError("Object does not implement the 'List' interface"); } } // --- Byte Arrays --- DART_EXPORT bool Dart_IsByteArray(Dart_Handle object) { return RawObject::IsByteArrayClassId(Api::ClassId(object)); } DART_EXPORT bool Dart_IsByteArrayExternal(Dart_Handle object) { return RawObject::IsExternalByteArrayClassId(Api::ClassId(object)); } DART_EXPORT Dart_Handle Dart_NewByteArray(intptr_t length) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_LENGTH(length, Uint8Array::kMaxElements); CHECK_CALLBACK_STATE(isolate); return Api::NewHandle(isolate, Uint8Array::New(length)); } DART_EXPORT Dart_Handle Dart_NewExternalByteArray( uint8_t* data, intptr_t length, void* peer, Dart_WeakPersistentHandleFinalizer callback) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (data == NULL && length != 0) { RETURN_NULL_ERROR(data); } CHECK_LENGTH(length, ExternalUint8Array::kMaxElements); CHECK_CALLBACK_STATE(isolate); const ExternalUint8Array& obj = ExternalUint8Array::Handle( ExternalUint8Array::New(data, length)); return reinterpret_cast(obj.AddFinalizer(peer, callback)); } DART_EXPORT Dart_Handle Dart_NewExternalClampedByteArray( uint8_t* data, intptr_t length, void* peer, Dart_WeakPersistentHandleFinalizer callback) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (data == NULL && length != 0) { RETURN_NULL_ERROR(data); } CHECK_LENGTH(length, ExternalUint8ClampedArray::kMaxElements); CHECK_CALLBACK_STATE(isolate); const ExternalUint8ClampedArray& obj = ExternalUint8ClampedArray::Handle( ExternalUint8ClampedArray::New(data, length)); return reinterpret_cast(obj.AddFinalizer(peer, callback)); } DART_EXPORT Dart_Handle Dart_ExternalByteArrayGetData(Dart_Handle object, void** data) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const ExternalUint8Array& array = Api::UnwrapExternalUint8ArrayHandle(isolate, object); if (array.IsNull()) { RETURN_TYPE_ERROR(isolate, object, ExternalUint8Array); } if (data == NULL) { RETURN_NULL_ERROR(data); } *data = array.GetData(); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_ExternalByteArrayGetPeer(Dart_Handle object, void** peer) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const ExternalUint8Array& array = Api::UnwrapExternalUint8ArrayHandle(isolate, object); if (array.IsNull()) { RETURN_TYPE_ERROR(isolate, object, ExternalUint8Array); } if (peer == NULL) { RETURN_NULL_ERROR(peer); } *peer = array.GetPeer(); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_ScalarListAcquireData(Dart_Handle array, Dart_Scalar_Type* type, void** data, intptr_t* len) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); intptr_t class_id = Api::ClassId(array); if (!RawObject::IsByteArrayClassId(class_id)) { RETURN_TYPE_ERROR(isolate, array, 'scalar list'); } if (type == NULL) { RETURN_NULL_ERROR(type); } if (data == NULL) { RETURN_NULL_ERROR(data); } if (len == NULL) { RETURN_NULL_ERROR(len); } // Get the type of typed array. switch (class_id) { case kByteArrayCid : *type = kByteArray; break; case kInt8ArrayCid : case kExternalInt8ArrayCid : *type = kInt8; break; case kUint8ArrayCid : case kExternalUint8ArrayCid : *type = kUint8; break; case kUint8ClampedArrayCid : case kExternalUint8ClampedArrayCid : *type = kUint8Clamped; break; case kInt16ArrayCid : case kExternalInt16ArrayCid : *type = kInt16; break; case kUint16ArrayCid : case kExternalUint16ArrayCid : *type = kUint16; break; case kInt32ArrayCid : case kExternalInt32ArrayCid : *type = kInt32; break; case kUint32ArrayCid : case kExternalUint32ArrayCid : *type = kUint32; break; case kInt64ArrayCid : case kExternalInt64ArrayCid : *type = kInt64; break; case kUint64ArrayCid : case kExternalUint64ArrayCid : *type = kUint64; break; case kFloat32ArrayCid : case kExternalFloat32ArrayCid : *type = kFloat32; break; case kFloat64ArrayCid : case kExternalFloat64ArrayCid : *type = kFloat64; break; } const ByteArray& obj = Api::UnwrapByteArrayHandle(isolate, array); ASSERT(!obj.IsNull()); *len = obj.Length(); // If it is an external typed array object just return the data field. if (RawObject::IsExternalByteArrayClassId(class_id)) { *data = reinterpret_cast(obj.ByteAddr(0)); } else { // Regular typed array object, set up some GC and API callback guards. isolate->IncrementNoGCScopeDepth(); START_NO_CALLBACK_SCOPE(isolate); *data = reinterpret_cast(obj.ByteAddr(0)); } return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_ScalarListReleaseData(Dart_Handle array) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); intptr_t class_id = Api::ClassId(array); if (!RawObject::IsByteArrayClassId(class_id)) { RETURN_TYPE_ERROR(isolate, array, 'scalar list'); } if (!RawObject::IsExternalByteArrayClassId(class_id)) { isolate->DecrementNoGCScopeDepth(); END_NO_CALLBACK_SCOPE(isolate); } return Api::Success(isolate); } // --- Closures --- 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. Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Instance& closure_obj = Api::UnwrapInstanceHandle(isolate, object); return (!closure_obj.IsNull() && closure_obj.IsClosure()); } DART_EXPORT Dart_Handle Dart_ClosureFunction(Dart_Handle closure) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Instance& closure_obj = Api::UnwrapInstanceHandle(isolate, closure); if (closure_obj.IsNull() || !closure_obj.IsClosure()) { RETURN_TYPE_ERROR(isolate, closure, Instance); } ASSERT(ClassFinalizer::AllClassesFinalized()); RawFunction* rf = Closure::function(closure_obj); return Api::NewHandle(isolate, rf); } DART_EXPORT Dart_Handle Dart_InvokeClosure(Dart_Handle closure, int number_of_arguments, Dart_Handle* arguments) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); const Instance& closure_obj = Api::UnwrapInstanceHandle(isolate, closure); if (closure_obj.IsNull() || !closure_obj.IsCallable(NULL, NULL)) { RETURN_TYPE_ERROR(isolate, closure, Instance); } if (number_of_arguments < 0) { return Api::NewError( "%s expects argument 'number_of_arguments' to be non-negative.", CURRENT_FUNC); } ASSERT(ClassFinalizer::AllClassesFinalized()); // Set up arguments to include the closure as the first argument. const Array& args = Array::Handle(isolate, Array::New(number_of_arguments + 1)); Object& obj = Object::Handle(isolate); args.SetAt(0, closure_obj); for (int i = 0; i < number_of_arguments; i++) { obj = Api::UnwrapHandle(arguments[i]); if (!obj.IsNull() && !obj.IsInstance()) { RETURN_TYPE_ERROR(isolate, arguments[i], Instance); } args.SetAt(i + 1, obj); } // Now try to invoke the closure. return Api::NewHandle(isolate, DartEntry::InvokeClosure(closure_obj, args)); } // --- Classes --- DART_EXPORT bool Dart_IsClass(Dart_Handle handle) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(handle)); return obj.IsClass(); } DART_EXPORT bool Dart_IsAbstractClass(Dart_Handle handle) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(handle)); if (obj.IsClass()) { return Class::Cast(obj).is_abstract(); } return false; } DART_EXPORT Dart_Handle Dart_ClassName(Dart_Handle clazz) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Class& cls = Api::UnwrapClassHandle(isolate, clazz); if (cls.IsNull()) { RETURN_TYPE_ERROR(isolate, clazz, Class); } return Api::NewHandle(isolate, cls.UserVisibleName()); } DART_EXPORT Dart_Handle Dart_ClassGetLibrary(Dart_Handle clazz) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Class& cls = Api::UnwrapClassHandle(isolate, clazz); if (cls.IsNull()) { RETURN_TYPE_ERROR(isolate, clazz, Class); } #if defined(DEBUG) const Library& lib = Library::Handle(cls.library()); if (lib.IsNull()) { // ASSERT(cls.IsDynamicClass() || cls.IsVoidClass()); if (!cls.IsDynamicClass() && !cls.IsVoidClass()) { fprintf(stderr, "NO LIBRARY: %s\n", cls.ToCString()); } } #endif return Api::NewHandle(isolate, cls.library()); } DART_EXPORT Dart_Handle Dart_ClassGetInterfaceCount(Dart_Handle clazz, intptr_t* count) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Class& cls = Api::UnwrapClassHandle(isolate, clazz); if (cls.IsNull()) { RETURN_TYPE_ERROR(isolate, clazz, Class); } const Array& interface_types = Array::Handle(isolate, cls.interfaces()); if (interface_types.IsNull()) { *count = 0; } else { *count = interface_types.Length(); } return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_ClassGetInterfaceAt(Dart_Handle clazz, intptr_t index) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Class& cls = Api::UnwrapClassHandle(isolate, clazz); if (cls.IsNull()) { RETURN_TYPE_ERROR(isolate, clazz, Class); } // Finalize all classes. Dart_Handle state = Api::CheckIsolateState(isolate); if (::Dart_IsError(state)) { return state; } const Array& interface_types = Array::Handle(isolate, cls.interfaces()); if (index < 0 || index >= interface_types.Length()) { return Api::NewError("%s: argument 'index' out of bounds.", CURRENT_FUNC); } Type& interface_type = Type::Handle(isolate); interface_type ^= interface_types.At(index); if (interface_type.HasResolvedTypeClass()) { return Api::NewHandle(isolate, interface_type.type_class()); } const String& type_name = String::Handle(isolate, interface_type.TypeClassName()); return Api::NewError("%s: internal error: found unresolved type class '%s'.", CURRENT_FUNC, type_name.ToCString()); } DART_EXPORT bool Dart_ClassIsTypedef(Dart_Handle clazz) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Class& cls = Api::UnwrapClassHandle(isolate, clazz); if (cls.IsNull()) { RETURN_TYPE_ERROR(isolate, clazz, Class); } // For now we represent typedefs as non-canonical signature classes. // I anticipate this may change if we make typedefs more general. return cls.IsSignatureClass() && !cls.IsCanonicalSignatureClass(); } DART_EXPORT Dart_Handle Dart_ClassGetTypedefReferent(Dart_Handle clazz) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Class& cls = Api::UnwrapClassHandle(isolate, clazz); if (cls.IsNull()) { RETURN_TYPE_ERROR(isolate, clazz, Class); } if (!cls.IsSignatureClass() && !cls.IsCanonicalSignatureClass()) { const String& cls_name = String::Handle(cls.UserVisibleName()); return Api::NewError("%s: class '%s' is not a typedef class. " "See Dart_ClassIsTypedef.", CURRENT_FUNC, cls_name.ToCString()); } const Function& func = Function::Handle(isolate, cls.signature_function()); return Api::NewHandle(isolate, func.signature_class()); } DART_EXPORT bool Dart_ClassIsFunctionType(Dart_Handle clazz) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Class& cls = Api::UnwrapClassHandle(isolate, clazz); if (cls.IsNull()) { RETURN_TYPE_ERROR(isolate, clazz, Class); } // A class represents a function type when it is a canonical // signature class. return cls.IsCanonicalSignatureClass(); } DART_EXPORT Dart_Handle Dart_ClassGetFunctionTypeSignature(Dart_Handle clazz) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Class& cls = Api::UnwrapClassHandle(isolate, clazz); if (cls.IsNull()) { RETURN_TYPE_ERROR(isolate, clazz, Class); } if (!cls.IsCanonicalSignatureClass()) { const String& cls_name = String::Handle(cls.UserVisibleName()); return Api::NewError("%s: class '%s' is not a function-type class. " "See Dart_ClassIsFunctionType.", CURRENT_FUNC, cls_name.ToCString()); } return Api::NewHandle(isolate, cls.signature_function()); } // --- Function and Variable Reflection --- // Outside of the vm, we expose setter names with a trailing '='. static bool HasExternalSetterSuffix(const String& name) { return name.CharAt(name.Length() - 1) == '='; } static RawString* RemoveExternalSetterSuffix(const String& name) { ASSERT(HasExternalSetterSuffix(name)); return String::SubString(name, 0, name.Length() - 1); } DART_EXPORT Dart_Handle Dart_GetFunctionNames(Dart_Handle target) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target)); if (obj.IsError()) { return target; } const GrowableObjectArray& names = GrowableObjectArray::Handle(isolate, GrowableObjectArray::New()); Function& func = Function::Handle(); String& name = String::Handle(); if (obj.IsClass()) { const Class& cls = Class::Cast(obj); const Array& func_array = Array::Handle(cls.functions()); // Some special types like 'dynamic' have a null functions list. if (!func_array.IsNull()) { for (intptr_t i = 0; i < func_array.Length(); ++i) { func ^= func_array.At(i); // Skip implicit getters and setters. if (func.kind() == RawFunction::kImplicitGetter || func.kind() == RawFunction::kImplicitSetter || func.kind() == RawFunction::kConstImplicitGetter || func.kind() == RawFunction::kMethodExtractor) { continue; } name = func.UserVisibleName(); names.Add(name); } } } else if (obj.IsLibrary()) { const Library& lib = Library::Cast(obj); DictionaryIterator it(lib); Object& obj = Object::Handle(); while (it.HasNext()) { obj = it.GetNext(); if (obj.IsFunction()) { func ^= obj.raw(); name = func.UserVisibleName(); names.Add(name); } } } else { return Api::NewError( "%s expects argument 'target' to be a class or library.", CURRENT_FUNC); } return Api::NewHandle(isolate, Array::MakeArray(names)); } DART_EXPORT Dart_Handle Dart_LookupFunction(Dart_Handle target, Dart_Handle function_name) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target)); if (obj.IsError()) { return target; } const String& func_name = Api::UnwrapStringHandle(isolate, function_name); if (func_name.IsNull()) { RETURN_TYPE_ERROR(isolate, function_name, String); } Function& func = Function::Handle(isolate); String& tmp_name = String::Handle(isolate); if (obj.IsClass()) { const Class& cls = Class::Cast(obj); // Case 1. Lookup the unmodified function name. func = cls.LookupFunctionAllowPrivate(func_name); // Case 2. Lookup the function without the external setter suffix // '='. Make sure to do this check after the regular lookup, so // that we don't interfere with operator lookups (like ==). if (func.IsNull() && HasExternalSetterSuffix(func_name)) { tmp_name = RemoveExternalSetterSuffix(func_name); tmp_name = Field::SetterName(tmp_name); func = cls.LookupFunctionAllowPrivate(tmp_name); } // Case 3. Lookup the funciton with the getter prefix prepended. if (func.IsNull()) { tmp_name = Field::GetterName(func_name); func = cls.LookupFunctionAllowPrivate(tmp_name); } // Case 4. Lookup the function with a . appended to find the // unnamed constructor. if (func.IsNull()) { tmp_name = String::Concat(func_name, Symbols::Dot()); func = cls.LookupFunctionAllowPrivate(tmp_name); } } else if (obj.IsLibrary()) { const Library& lib = Library::Cast(obj); // Case 1. Lookup the unmodified function name. func = lib.LookupFunctionAllowPrivate(func_name); // Case 2. Lookup the function without the external setter suffix // '='. Make sure to do this check after the regular lookup, so // that we don't interfere with operator lookups (like ==). if (func.IsNull() && HasExternalSetterSuffix(func_name)) { tmp_name = RemoveExternalSetterSuffix(func_name); tmp_name = Field::SetterName(tmp_name); func = lib.LookupFunctionAllowPrivate(tmp_name); } // Case 3. Lookup the function with the getter prefix prepended. if (func.IsNull()) { tmp_name = Field::GetterName(func_name); func = lib.LookupFunctionAllowPrivate(tmp_name); } } else { return Api::NewError( "%s expects argument 'target' to be a class or library.", CURRENT_FUNC); } #if defined(DEBUG) if (!func.IsNull()) { // We only provide access to a subset of function kinds. RawFunction::Kind func_kind = func.kind(); ASSERT(func_kind == RawFunction::kRegularFunction || func_kind == RawFunction::kGetterFunction || func_kind == RawFunction::kSetterFunction || func_kind == RawFunction::kConstructor); } #endif return Api::NewHandle(isolate, func.raw()); } DART_EXPORT bool Dart_IsFunction(Dart_Handle handle) { return Api::ClassId(handle) == kFunctionCid; } DART_EXPORT Dart_Handle Dart_FunctionName(Dart_Handle function) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Function& func = Api::UnwrapFunctionHandle(isolate, function); if (func.IsNull()) { RETURN_TYPE_ERROR(isolate, function, Function); } return Api::NewHandle(isolate, func.UserVisibleName()); } DART_EXPORT Dart_Handle Dart_FunctionOwner(Dart_Handle function) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Function& func = Api::UnwrapFunctionHandle(isolate, function); if (func.IsNull()) { RETURN_TYPE_ERROR(isolate, function, Function); } if (func.IsNonImplicitClosureFunction()) { RawFunction* parent_function = func.parent_function(); return Api::NewHandle(isolate, parent_function); } const Class& owner = Class::Handle(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(owner.library()); if (lib.IsNull()) { ASSERT(owner.IsDynamicClass() || owner.IsVoidClass()); } #endif return Api::NewHandle(isolate, owner.library()); } else { return Api::NewHandle(isolate, owner.raw()); } } DART_EXPORT Dart_Handle Dart_FunctionIsAbstract(Dart_Handle function, bool* is_abstract) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (is_abstract == NULL) { RETURN_NULL_ERROR(is_abstract); } const Function& func = Api::UnwrapFunctionHandle(isolate, function); if (func.IsNull()) { RETURN_TYPE_ERROR(isolate, function, Function); } *is_abstract = func.is_abstract(); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_FunctionIsStatic(Dart_Handle function, bool* is_static) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (is_static == NULL) { RETURN_NULL_ERROR(is_static); } const Function& func = Api::UnwrapFunctionHandle(isolate, function); if (func.IsNull()) { RETURN_TYPE_ERROR(isolate, function, Function); } *is_static = func.is_static(); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_FunctionIsConstructor(Dart_Handle function, bool* is_constructor) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (is_constructor == NULL) { RETURN_NULL_ERROR(is_constructor); } const Function& func = Api::UnwrapFunctionHandle(isolate, function); if (func.IsNull()) { RETURN_TYPE_ERROR(isolate, function, Function); } *is_constructor = func.kind() == RawFunction::kConstructor; return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_FunctionIsGetter(Dart_Handle function, bool* is_getter) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (is_getter == NULL) { RETURN_NULL_ERROR(is_getter); } const Function& func = Api::UnwrapFunctionHandle(isolate, function); if (func.IsNull()) { RETURN_TYPE_ERROR(isolate, function, Function); } *is_getter = func.IsGetterFunction(); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_FunctionIsSetter(Dart_Handle function, bool* is_setter) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (is_setter == NULL) { RETURN_NULL_ERROR(is_setter); } const Function& func = Api::UnwrapFunctionHandle(isolate, function); if (func.IsNull()) { RETURN_TYPE_ERROR(isolate, function, Function); } *is_setter = (func.kind() == RawFunction::kSetterFunction); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_FunctionReturnType(Dart_Handle function) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Function& func = Api::UnwrapFunctionHandle(isolate, function); if (func.IsNull()) { RETURN_TYPE_ERROR(isolate, function, Function); } if (func.kind() == RawFunction::kConstructor) { // Special case the return type for constructors. Inside the vm // we mark them as returning dynamic, but for the purposes of // reflection, they return the type of the class being // constructed. return Api::NewHandle(isolate, func.Owner()); } else { const AbstractType& return_type = AbstractType::Handle(isolate, func.result_type()); return TypeToHandle(isolate, "Dart_FunctionReturnType", return_type); } } DART_EXPORT Dart_Handle Dart_FunctionParameterCounts( Dart_Handle function, int64_t* fixed_param_count, int64_t* opt_param_count) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (fixed_param_count == NULL) { RETURN_NULL_ERROR(fixed_param_count); } if (opt_param_count == NULL) { RETURN_NULL_ERROR(opt_param_count); } const Function& func = Api::UnwrapFunctionHandle(isolate, function); if (func.IsNull()) { RETURN_TYPE_ERROR(isolate, function, Function); } // We hide implicit parameters, such as a method's receiver. This is // consistent with Invoke or New, which don't expect their callers to // provide them in the argument lists they are handed. *fixed_param_count = func.num_fixed_parameters() - func.NumImplicitParameters(); // TODO(regis): Separately report named and positional optional param counts. *opt_param_count = func.NumOptionalParameters(); ASSERT(*fixed_param_count >= 0); ASSERT(*opt_param_count >= 0); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_FunctionParameterType(Dart_Handle function, int parameter_index) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Function& func = Api::UnwrapFunctionHandle(isolate, function); if (func.IsNull()) { RETURN_TYPE_ERROR(isolate, function, Function); } const intptr_t num_implicit_params = func.NumImplicitParameters(); const intptr_t num_params = func.NumParameters() - num_implicit_params; if (parameter_index < 0 || parameter_index >= num_params) { return Api::NewError( "%s: argument 'parameter_index' out of range. " "Expected 0..%"Pd" but saw %d.", CURRENT_FUNC, num_params, parameter_index); } const AbstractType& param_type = AbstractType::Handle(isolate, func.ParameterTypeAt( num_implicit_params + parameter_index)); return TypeToHandle(isolate, "Dart_FunctionParameterType", param_type); } DART_EXPORT Dart_Handle Dart_GetVariableNames(Dart_Handle target) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target)); if (obj.IsError()) { return target; } const GrowableObjectArray& names = GrowableObjectArray::Handle(isolate, GrowableObjectArray::New()); Field& field = Field::Handle(isolate); String& name = String::Handle(isolate); if (obj.IsClass()) { const Class& cls = Class::Cast(obj); const Array& field_array = Array::Handle(cls.fields()); // Some special types like 'dynamic' have a null fields list. // // TODO(turnidge): Fix 'dynamic' so that it does not have a null // fields list. This will have to wait until the empty array is // allocated in the vm isolate. if (!field_array.IsNull()) { for (intptr_t i = 0; i < field_array.Length(); ++i) { field ^= field_array.At(i); name = field.UserVisibleName(); names.Add(name); } } } else if (obj.IsLibrary()) { const Library& lib = Library::Cast(obj); DictionaryIterator it(lib); Object& obj = Object::Handle(isolate); while (it.HasNext()) { obj = it.GetNext(); if (obj.IsField()) { field ^= obj.raw(); name = field.UserVisibleName(); names.Add(name); } } } else { return Api::NewError( "%s expects argument 'target' to be a class or library.", CURRENT_FUNC); } return Api::NewHandle(isolate, Array::MakeArray(names)); } DART_EXPORT Dart_Handle Dart_LookupVariable(Dart_Handle target, Dart_Handle variable_name) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target)); if (obj.IsError()) { return target; } const String& var_name = Api::UnwrapStringHandle(isolate, variable_name); if (var_name.IsNull()) { RETURN_TYPE_ERROR(isolate, variable_name, String); } if (obj.IsClass()) { const Class& cls = Class::Cast(obj); return Api::NewHandle(isolate, cls.LookupField(var_name)); } if (obj.IsLibrary()) { const Library& lib = Library::Cast(obj); return Api::NewHandle(isolate, lib.LookupFieldAllowPrivate(var_name)); } return Api::NewError( "%s expects argument 'target' to be a class or library.", CURRENT_FUNC); } DART_EXPORT bool Dart_IsVariable(Dart_Handle handle) { return Api::ClassId(handle) == kFieldCid; } DART_EXPORT Dart_Handle Dart_VariableName(Dart_Handle variable) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Field& var = Api::UnwrapFieldHandle(isolate, variable); if (var.IsNull()) { RETURN_TYPE_ERROR(isolate, variable, Field); } return Api::NewHandle(isolate, var.UserVisibleName()); } DART_EXPORT Dart_Handle Dart_VariableIsStatic(Dart_Handle variable, bool* is_static) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (is_static == NULL) { RETURN_NULL_ERROR(is_static); } const Field& var = Api::UnwrapFieldHandle(isolate, variable); if (var.IsNull()) { RETURN_TYPE_ERROR(isolate, variable, Field); } *is_static = var.is_static(); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_VariableIsFinal(Dart_Handle variable, bool* is_final) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); if (is_final == NULL) { RETURN_NULL_ERROR(is_final); } const Field& var = Api::UnwrapFieldHandle(isolate, variable); if (var.IsNull()) { RETURN_TYPE_ERROR(isolate, variable, Field); } *is_final = var.is_final(); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_VariableType(Dart_Handle variable) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Field& var = Api::UnwrapFieldHandle(isolate, variable); if (var.IsNull()) { RETURN_TYPE_ERROR(isolate, variable, Field); } const AbstractType& type = AbstractType::Handle(isolate, var.type()); return TypeToHandle(isolate, "Dart_VariableType", type); } DART_EXPORT Dart_Handle Dart_GetTypeVariableNames(Dart_Handle clazz) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Class& cls = Api::UnwrapClassHandle(isolate, clazz); if (cls.IsNull()) { RETURN_TYPE_ERROR(isolate, clazz, Class); } const intptr_t num_type_params = cls.NumTypeParameters(); const TypeArguments& type_params = TypeArguments::Handle(cls.type_parameters()); const GrowableObjectArray& names = GrowableObjectArray::Handle(isolate, GrowableObjectArray::New()); TypeParameter& type_param = TypeParameter::Handle(isolate); String& name = String::Handle(isolate); for (intptr_t i = 0; i < num_type_params; i++) { type_param ^= type_params.TypeAt(i); name = type_param.name(); names.Add(name); } return Api::NewHandle(isolate, Array::MakeArray(names)); } DART_EXPORT Dart_Handle Dart_LookupTypeVariable( Dart_Handle clazz, Dart_Handle type_variable_name) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Class& cls = Api::UnwrapClassHandle(isolate, clazz); if (cls.IsNull()) { RETURN_TYPE_ERROR(isolate, clazz, Class); } const String& var_name = Api::UnwrapStringHandle(isolate, type_variable_name); if (var_name.IsNull()) { RETURN_TYPE_ERROR(isolate, type_variable_name, String); } const intptr_t num_type_params = cls.NumTypeParameters(); const TypeArguments& type_params = TypeArguments::Handle(cls.type_parameters()); TypeParameter& type_param = TypeParameter::Handle(isolate); String& name = String::Handle(isolate); for (intptr_t i = 0; i < num_type_params; i++) { type_param ^= type_params.TypeAt(i); name = type_param.name(); if (name.Equals(var_name)) { return Api::NewHandle(isolate, type_param.raw()); } } const String& cls_name = String::Handle(cls.UserVisibleName()); return Api::NewError( "%s: Could not find type variable named '%s' for class %s.\n", CURRENT_FUNC, var_name.ToCString(), cls_name.ToCString()); } DART_EXPORT bool Dart_IsTypeVariable(Dart_Handle handle) { return Api::ClassId(handle) == kTypeParameterCid; } DART_EXPORT Dart_Handle Dart_TypeVariableName(Dart_Handle type_variable) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const TypeParameter& type_var = Api::UnwrapTypeParameterHandle(isolate, type_variable); if (type_var.IsNull()) { RETURN_TYPE_ERROR(isolate, type_variable, TypeParameter); } return Api::NewHandle(isolate, type_var.name()); } DART_EXPORT Dart_Handle Dart_TypeVariableOwner(Dart_Handle type_variable) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const TypeParameter& type_var = Api::UnwrapTypeParameterHandle(isolate, type_variable); if (type_var.IsNull()) { RETURN_TYPE_ERROR(isolate, type_variable, TypeParameter); } const Class& owner = Class::Handle(type_var.parameterized_class()); ASSERT(!owner.IsNull() && owner.IsClass()); return Api::NewHandle(isolate, owner.raw()); } DART_EXPORT Dart_Handle Dart_TypeVariableUpperBound(Dart_Handle type_variable) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const TypeParameter& type_var = Api::UnwrapTypeParameterHandle(isolate, type_variable); if (type_var.IsNull()) { RETURN_TYPE_ERROR(isolate, type_variable, TypeParameter); } const AbstractType& bound = AbstractType::Handle(type_var.bound()); return TypeToHandle(isolate, "Dart_TypeVariableUpperBound", bound); } // --- Constructors, Methods, and Fields --- static RawObject* ResolveConstructor(const char* current_func, const Class& cls, const String& class_name, const String& dotted_name, int num_args) { // The constructor must be present in the interface. String& constr_name = String::Handle(String::Concat(class_name, dotted_name)); const Function& constructor = Function::Handle(cls.LookupFunctionAllowPrivate(constr_name)); if (constructor.IsNull() || (!constructor.IsConstructor() && !constructor.IsFactory())) { const String& lookup_class_name = String::Handle(cls.Name()); if (!class_name.Equals(lookup_class_name)) { // When the class name used to build the constructor name is // different than the name of the class in which we are doing // the lookup, it can be confusing to the user to figure out // what's going on. Be a little more explicit for these error // messages. const String& message = String::Handle( String::NewFormatted( "%s: could not find factory '%s' in class '%s'.", current_func, constr_name.ToCString(), lookup_class_name.ToCString())); return ApiError::New(message); } else { const String& message = String::Handle( String::NewFormatted("%s: could not find constructor '%s'.", current_func, constr_name.ToCString())); return ApiError::New(message); } } int extra_args = (constructor.IsConstructor() ? 2 : 1); String& error_message = String::Handle(); if (!constructor.AreValidArgumentCounts(num_args + extra_args, 0, &error_message)) { const String& message = String::Handle( String::NewFormatted("%s: wrong argument count for " "constructor '%s': %s.", current_func, constr_name.ToCString(), error_message.ToCString())); return ApiError::New(message); } return constructor.raw(); } DART_EXPORT Dart_Handle Dart_New(Dart_Handle clazz, Dart_Handle constructor_name, int number_of_arguments, Dart_Handle* arguments) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); Object& result = Object::Handle(isolate); if (number_of_arguments < 0) { return Api::NewError( "%s expects argument 'number_of_arguments' to be non-negative.", CURRENT_FUNC); } // Get the class to instantiate. Class& cls = Class::Handle(isolate, Api::UnwrapClassHandle(isolate, clazz).raw()); if (cls.IsNull()) { RETURN_TYPE_ERROR(isolate, clazz, Class); } String& base_constructor_name = String::Handle(); base_constructor_name = cls.Name(); // And get the name of the constructor to invoke. String& dot_name = String::Handle(isolate); const Object& name_obj = Object::Handle(isolate, Api::UnwrapHandle(constructor_name)); if (name_obj.IsNull()) { dot_name = Symbols::Dot().raw(); } else if (name_obj.IsString()) { dot_name = String::Concat(Symbols::Dot(), String::Cast(name_obj)); } else { RETURN_TYPE_ERROR(isolate, constructor_name, String); } Dart_Handle state = Api::CheckIsolateState(isolate); if (::Dart_IsError(state)) { return state; } // Resolve the constructor. result = ResolveConstructor( "Dart_New", cls, base_constructor_name, dot_name, number_of_arguments); if (result.IsError()) { return Api::NewHandle(isolate, result.raw()); } // TODO(turnidge): Support redirecting factories. ASSERT(result.IsFunction()); Function& constructor = Function::Handle(isolate); constructor ^= result.raw(); Instance& new_object = Instance::Handle(isolate); if (constructor.IsConstructor()) { // Create the new object. new_object = Instance::New(cls); } // Create the argument list. intptr_t arg_index = 0; int extra_args = (constructor.IsConstructor() ? 2 : 1); const Array& args = Array::Handle(isolate, Array::New(number_of_arguments + extra_args)); if (constructor.IsConstructor()) { // Constructors get the uninitialized object and a constructor phase. args.SetAt(arg_index++, new_object); args.SetAt(arg_index++, Smi::Handle(isolate, Smi::New(Function::kCtorPhaseAll))); } else { // Factories get type arguments. args.SetAt(arg_index++, TypeArguments::Handle(isolate)); } Object& argument = Object::Handle(isolate); for (int i = 0; i < number_of_arguments; i++) { argument = Api::UnwrapHandle(arguments[i]); if (!argument.IsNull() && !argument.IsInstance()) { if (argument.IsError()) { return Api::NewHandle(isolate, argument.raw()); } else { return Api::NewError( "%s expects arguments[%d] to be an Instance handle.", CURRENT_FUNC, i); } } args.SetAt(arg_index++, argument); } // Invoke the constructor and return the new object. result = DartEntry::InvokeStatic(constructor, args); if (result.IsError()) { return Api::NewHandle(isolate, result.raw()); } if (constructor.IsConstructor()) { ASSERT(result.IsNull()); } else { ASSERT(result.IsNull() || result.IsInstance()); new_object ^= result.raw(); } return Api::NewHandle(isolate, new_object.raw()); } DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target, Dart_Handle name, int number_of_arguments, Dart_Handle* arguments) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); const String& function_name = Api::UnwrapStringHandle(isolate, name); if (function_name.IsNull()) { RETURN_TYPE_ERROR(isolate, name, String); } if (number_of_arguments < 0) { return Api::NewError( "%s expects argument 'number_of_arguments' to be non-negative.", CURRENT_FUNC); } const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(target)); if (obj.IsError()) { return target; } // Check for malformed arguments in the arguments list. intptr_t num_receiver = (obj.IsNull() || obj.IsInstance()) ? 1 : 0; const Array& args = Array::Handle(isolate, Array::New(number_of_arguments + num_receiver)); Object& arg = Object::Handle(isolate); for (int i = 0; i < number_of_arguments; i++) { arg = Api::UnwrapHandle(arguments[i]); if (!arg.IsNull() && !arg.IsInstance()) { if (arg.IsError()) { return Api::NewHandle(isolate, arg.raw()); } else { return Api::NewError( "%s expects arguments[%d] to be an Instance handle.", CURRENT_FUNC, i); } } args.SetAt((i + num_receiver), arg); } if (obj.IsNull() || obj.IsInstance()) { Instance& instance = Instance::Handle(isolate); instance ^= obj.raw(); const Function& function = Function::Handle( isolate, Resolver::ResolveDynamic(instance, function_name, (number_of_arguments + 1), Resolver::kIsQualified)); // TODO(5415268): Invoke noSuchMethod instead of failing. if (function.IsNull()) { const Type& type = Type::Handle(isolate, instance.GetType()); const String& cls_name = String::Handle(isolate, type.ClassName()); return Api::NewError("%s: did not find instance method '%s.%s'.", CURRENT_FUNC, cls_name.ToCString(), function_name.ToCString()); } args.SetAt(0, instance); return Api::NewHandle(isolate, DartEntry::InvokeDynamic(function, args)); } else if (obj.IsClass()) { // Finalize all classes. Dart_Handle state = Api::CheckIsolateState(isolate); if (::Dart_IsError(state)) { return state; } const Class& cls = Class::Cast(obj); const Function& function = Function::Handle( isolate, Resolver::ResolveStatic(cls, function_name, number_of_arguments, Object::empty_array(), Resolver::kIsQualified)); if (function.IsNull()) { const String& cls_name = String::Handle(isolate, cls.Name()); return Api::NewError("%s: did not find static method '%s.%s'.", CURRENT_FUNC, cls_name.ToCString(), function_name.ToCString()); } return Api::NewHandle(isolate, DartEntry::InvokeStatic(function, args)); } else if (obj.IsLibrary()) { // Check whether class finalization is needed. bool finalize_classes = true; const Library& lib = Library::Cast(obj); // When calling functions in the dart:builtin library do not finalize as it // should have been prefinalized. Library& builtin = Library::Handle(isolate, isolate->object_store()->builtin_library()); if (builtin.raw() == lib.raw()) { finalize_classes = false; } // Finalize all classes if needed. if (finalize_classes) { Dart_Handle state = Api::CheckIsolateState(isolate); if (::Dart_IsError(state)) { return state; } } Function& function = Function::Handle(isolate); function = lib.LookupFunctionAllowPrivate(function_name); if (function.IsNull()) { return Api::NewError("%s: did not find top-level function '%s'.", CURRENT_FUNC, function_name.ToCString()); } // LookupFunctionAllowPrivate does not check argument arity, so we // do it here. String& error_message = String::Handle(); if (!function.AreValidArgumentCounts(number_of_arguments, 0, &error_message)) { return Api::NewError("%s: wrong argument count for function '%s': %s.", CURRENT_FUNC, function_name.ToCString(), error_message.ToCString()); } return Api::NewHandle(isolate, DartEntry::InvokeStatic(function, args)); } else { return Api::NewError( "%s expects argument 'target' to be an object, class, or library.", CURRENT_FUNC); } } static bool FieldIsUninitialized(Isolate* isolate, const Field& fld) { ASSERT(!fld.IsNull()); // Return getter method for uninitialized fields, rather than the // field object, since the value in the field object will not be // initialized until the first time the getter is invoked. const Instance& value = Instance::Handle(isolate, fld.value()); ASSERT(value.raw() != Object::transition_sentinel().raw()); return value.raw() == Object::sentinel().raw(); } DART_EXPORT Dart_Handle Dart_GetField(Dart_Handle container, Dart_Handle name) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); const String& field_name = Api::UnwrapStringHandle(isolate, name); if (field_name.IsNull()) { RETURN_TYPE_ERROR(isolate, name, String); } const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(container)); Field& field = Field::Handle(isolate); Function& getter = Function::Handle(isolate); if (obj.IsNull()) { return Api::NewError("%s expects argument 'container' to be non-null.", CURRENT_FUNC); } else if (obj.IsInstance()) { // Every instance field has a getter Function. Try to find the // getter in any superclass and use that function to access the // field. const Instance& instance = Instance::Cast(obj); Class& cls = Class::Handle(isolate, instance.clazz()); while (!cls.IsNull()) { String& getter_name = String::Handle(isolate, Field::GetterName(field_name)); getter = cls.LookupDynamicFunctionAllowPrivate(getter_name); if (!getter.IsNull()) { break; } cls = cls.SuperClass(); } if (getter.IsNull()) { return Api::NewError("%s: did not find instance field '%s'.", CURRENT_FUNC, field_name.ToCString()); } // Invoke the getter and return the result. const int kNumArgs = 1; const Array& args = Array::Handle(isolate, Array::New(kNumArgs)); args.SetAt(0, instance); return Api::NewHandle(isolate, DartEntry::InvokeDynamic(getter, args)); } else if (obj.IsClass()) { // Finalize all classes. Dart_Handle state = Api::CheckIsolateState(isolate); if (::Dart_IsError(state)) { return state; } // To access a static field we may need to use the Field or the // getter Function. const Class& cls = Class::Cast(obj); field = cls.LookupStaticField(field_name); if (field.IsNull() || FieldIsUninitialized(isolate, field)) { const String& getter_name = String::Handle(isolate, Field::GetterName(field_name)); getter = cls.LookupStaticFunctionAllowPrivate(getter_name); } if (!getter.IsNull()) { // Invoke the getter and return the result. return Api::NewHandle( isolate, DartEntry::InvokeStatic(getter, Object::empty_array())); } else if (!field.IsNull()) { return Api::NewHandle(isolate, field.value()); } else { return Api::NewError("%s: did not find static field '%s'.", CURRENT_FUNC, field_name.ToCString()); } } else if (obj.IsLibrary()) { // TODO(turnidge): Do we need to call CheckIsolateState here? // To access a top-level we may need to use the Field or the // getter Function. The getter function may either be in the // library or in the field's owner class, depending. const Library& lib = Library::Cast(obj); field = lib.LookupFieldAllowPrivate(field_name); if (field.IsNull()) { // No field found. Check for a getter in the lib. const String& getter_name = String::Handle(isolate, Field::GetterName(field_name)); getter = lib.LookupFunctionAllowPrivate(getter_name); } else if (FieldIsUninitialized(isolate, field)) { // A field was found. Check for a getter in the field's owner classs. const Class& cls = Class::Handle(isolate, field.owner()); const String& getter_name = String::Handle(isolate, Field::GetterName(field_name)); getter = cls.LookupStaticFunctionAllowPrivate(getter_name); } if (!getter.IsNull()) { // Invoke the getter and return the result. return Api::NewHandle( isolate, DartEntry::InvokeStatic(getter, Object::empty_array())); } else if (!field.IsNull()) { return Api::NewHandle(isolate, field.value()); } else { return Api::NewError("%s: did not find top-level variable '%s'.", CURRENT_FUNC, field_name.ToCString()); } } else if (obj.IsError()) { return container; } else { return Api::NewError( "%s expects argument 'container' to be an object, class, or library.", CURRENT_FUNC); } } DART_EXPORT Dart_Handle Dart_SetField(Dart_Handle container, Dart_Handle name, Dart_Handle value) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); CHECK_CALLBACK_STATE(isolate); const String& field_name = Api::UnwrapStringHandle(isolate, name); if (field_name.IsNull()) { RETURN_TYPE_ERROR(isolate, name, String); } // Since null is allowed for value, we don't use UnwrapInstanceHandle. const Object& value_obj = Object::Handle(isolate, Api::UnwrapHandle(value)); if (!value_obj.IsNull() && !value_obj.IsInstance()) { RETURN_TYPE_ERROR(isolate, value, Instance); } Instance& value_instance = Instance::Handle(isolate); value_instance ^= value_obj.raw(); Field& field = Field::Handle(isolate); Function& setter = Function::Handle(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(container)); if (obj.IsNull()) { return Api::NewError("%s expects argument 'container' to be non-null.", CURRENT_FUNC); } else if (obj.IsInstance()) { // Every instance field has a setter Function. Try to find the // setter in any superclass and use that function to access the // field. const Instance& instance = Instance::Cast(obj); Class& cls = Class::Handle(isolate, instance.clazz()); while (!cls.IsNull()) { field = cls.LookupInstanceField(field_name); if (!field.IsNull() && field.is_final()) { return Api::NewError("%s: cannot set final field '%s'.", CURRENT_FUNC, field_name.ToCString()); } String& setter_name = String::Handle(isolate, Field::SetterName(field_name)); setter = cls.LookupDynamicFunctionAllowPrivate(setter_name); if (!setter.IsNull()) { break; } cls = cls.SuperClass(); } if (setter.IsNull()) { return Api::NewError("%s: did not find instance field '%s'.", CURRENT_FUNC, field_name.ToCString()); } // Invoke the setter and return the result. const int kNumArgs = 2; const Array& args = Array::Handle(isolate, Array::New(kNumArgs)); args.SetAt(0, instance); args.SetAt(1, value_instance); return Api::NewHandle(isolate, DartEntry::InvokeDynamic(setter, args)); } else if (obj.IsClass()) { // To access a static field we may need to use the Field or the // setter Function. const Class& cls = Class::Cast(obj); field = cls.LookupStaticField(field_name); if (field.IsNull()) { String& setter_name = String::Handle(isolate, Field::SetterName(field_name)); setter = cls.LookupStaticFunctionAllowPrivate(setter_name); } if (!setter.IsNull()) { // Invoke the setter and return the result. const int kNumArgs = 1; const Array& args = Array::Handle(isolate, Array::New(kNumArgs)); args.SetAt(0, value_instance); const Object& result = Object::Handle(isolate, DartEntry::InvokeStatic(setter, args)); if (result.IsError()) { return Api::NewHandle(isolate, result.raw()); } else { return Api::Success(isolate); } } else if (!field.IsNull()) { if (field.is_final()) { return Api::NewError("%s: cannot set final field '%s'.", CURRENT_FUNC, field_name.ToCString()); } else { field.set_value(value_instance); return Api::Success(isolate); } } else { return Api::NewError("%s: did not find static field '%s'.", CURRENT_FUNC, field_name.ToCString()); } } else if (obj.IsLibrary()) { // To access a top-level we may need to use the Field or the // setter Function. The setter function may either be in the // library or in the field's owner class, depending. const Library& lib = Library::Cast(obj); field = lib.LookupFieldAllowPrivate(field_name); if (field.IsNull()) { const String& setter_name = String::Handle(isolate, Field::SetterName(field_name)); setter ^= lib.LookupFunctionAllowPrivate(setter_name); } if (!setter.IsNull()) { // Invoke the setter and return the result. const int kNumArgs = 1; const Array& args = Array::Handle(isolate, Array::New(kNumArgs)); args.SetAt(0, value_instance); const Object& result = Object::Handle(isolate, DartEntry::InvokeStatic(setter, args)); if (result.IsError()) { return Api::NewHandle(isolate, result.raw()); } else { return Api::Success(isolate); } } else if (!field.IsNull()) { if (field.is_final()) { return Api::NewError("%s: cannot set final top-level variable '%s'.", CURRENT_FUNC, field_name.ToCString()); } else { field.set_value(value_instance); return Api::Success(isolate); } } else { return Api::NewError("%s: did not find top-level variable '%s'.", CURRENT_FUNC, field_name.ToCString()); } } else if (obj.IsError()) { return container; } else { return Api::NewError( "%s expects argument 'container' to be an object, class, or library.", CURRENT_FUNC); } } DART_EXPORT Dart_Handle Dart_CreateNativeWrapperClass(Dart_Handle library, Dart_Handle name, int field_count) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const String& cls_name = Api::UnwrapStringHandle(isolate, name); if (cls_name.IsNull()) { RETURN_TYPE_ERROR(isolate, name, String); } const Library& lib = Api::UnwrapLibraryHandle(isolate, library); if (lib.IsNull()) { RETURN_TYPE_ERROR(isolate, library, Library); } if (field_count <= 0) { return Api::NewError( "Negative field_count passed to Dart_CreateNativeWrapperClass"); } CHECK_CALLBACK_STATE(isolate); String& cls_symbol = String::Handle(isolate, Symbols::New(cls_name)); const Class& cls = Class::Handle( isolate, Class::NewNativeWrapper(lib, cls_symbol, field_count)); if (cls.IsNull()) { return Api::NewError( "Unable to create native wrapper class : already exists"); } return Api::NewHandle(isolate, cls.raw()); } DART_EXPORT Dart_Handle Dart_GetNativeInstanceFieldCount(Dart_Handle obj, int* count) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Instance& instance = Api::UnwrapInstanceHandle(isolate, obj); if (instance.IsNull()) { RETURN_TYPE_ERROR(isolate, obj, Instance); } const Class& cls = Class::Handle(isolate, instance.clazz()); *count = cls.num_native_fields(); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_GetNativeInstanceField(Dart_Handle obj, int index, intptr_t* value) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Instance& instance = Api::UnwrapInstanceHandle(isolate, obj); if (instance.IsNull()) { RETURN_TYPE_ERROR(isolate, obj, Instance); } if (!instance.IsValidNativeIndex(index)) { return Api::NewError( "%s: invalid index %d passed in to access native instance field", CURRENT_FUNC, index); } *value = instance.GetNativeField(isolate, index); return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_SetNativeInstanceField(Dart_Handle obj, int index, intptr_t value) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Instance& instance = Api::UnwrapInstanceHandle(isolate, obj); if (instance.IsNull()) { RETURN_TYPE_ERROR(isolate, obj, Instance); } if (!instance.IsValidNativeIndex(index)) { return Api::NewError( "%s: invalid index %d passed in to set native instance field", CURRENT_FUNC, index); } instance.SetNativeField(index, value); return Api::Success(isolate); } // --- Exceptions ---- DART_EXPORT Dart_Handle Dart_ThrowException(Dart_Handle exception) { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); CHECK_CALLBACK_STATE(isolate); { const Instance& excp = Api::UnwrapInstanceHandle(isolate, exception); if (excp.IsNull()) { RETURN_TYPE_ERROR(isolate, exception, Instance); } } if (isolate->top_exit_frame_info() == 0) { // There are no dart frames on the stack so it would be illegal to // throw an exception here. return Api::NewError("No Dart frames on stack, cannot throw exception"); } // Unwind all the API scopes till the exit frame before throwing an // exception. ApiState* state = isolate->api_state(); ASSERT(state != NULL); const Instance* saved_exception; { NoGCScope no_gc; RawInstance* raw_exception = Api::UnwrapInstanceHandle(isolate, exception).raw(); state->UnwindScopes(isolate->top_exit_frame_info()); saved_exception = &Instance::Handle(raw_exception); } Exceptions::Throw(*saved_exception); return Api::NewError("Exception was not thrown, internal error"); } DART_EXPORT Dart_Handle Dart_ReThrowException(Dart_Handle exception, Dart_Handle stacktrace) { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); CHECK_CALLBACK_STATE(isolate); { const Instance& excp = Api::UnwrapInstanceHandle(isolate, exception); if (excp.IsNull()) { RETURN_TYPE_ERROR(isolate, exception, Instance); } const Instance& stk = Api::UnwrapInstanceHandle(isolate, stacktrace); if (stk.IsNull()) { RETURN_TYPE_ERROR(isolate, stacktrace, Instance); } } if (isolate->top_exit_frame_info() == 0) { // There are no dart frames on the stack so it would be illegal to // throw an exception here. return Api::NewError("No Dart frames on stack, cannot throw exception"); } // Unwind all the API scopes till the exit frame before throwing an // exception. ApiState* state = isolate->api_state(); ASSERT(state != NULL); const Instance* saved_exception; const Instance* saved_stacktrace; { NoGCScope no_gc; RawInstance* raw_exception = Api::UnwrapInstanceHandle(isolate, exception).raw(); RawInstance* raw_stacktrace = Api::UnwrapInstanceHandle(isolate, stacktrace).raw(); state->UnwindScopes(isolate->top_exit_frame_info()); saved_exception = &Instance::Handle(raw_exception); saved_stacktrace = &Instance::Handle(raw_stacktrace); } Exceptions::ReThrow(*saved_exception, *saved_stacktrace); return Api::NewError("Exception was not re thrown, internal error"); } // --- Native functions --- DART_EXPORT Dart_Handle Dart_GetNativeArgument(Dart_NativeArguments args, int index) { NativeArguments* arguments = reinterpret_cast(args); if ((index < 0) || (index >= arguments->NativeArgCount())) { return Api::NewError( "%s: argument 'index' out of range. Expected 0..%d but saw %d.", CURRENT_FUNC, arguments->NativeArgCount() - 1, index); } return Api::NewHandle(arguments->isolate(), arguments->NativeArgAt(index)); } DART_EXPORT int Dart_GetNativeArgumentCount(Dart_NativeArguments args) { NativeArguments* arguments = reinterpret_cast(args); return arguments->NativeArgCount(); } // This function has friend access to SetReturnUnsafe. void SetReturnValueHelper(Dart_NativeArguments args, Dart_Handle retval) { NoGCScope no_gc_scope; NativeArguments* arguments = reinterpret_cast(args); arguments->SetReturnUnsafe(Api::UnwrapHandle(retval)); } DART_EXPORT void Dart_SetReturnValue(Dart_NativeArguments args, Dart_Handle retval) { SetReturnValueHelper(args, retval); } // --- Scripts and Libraries --- DART_EXPORT Dart_Handle Dart_SetLibraryTagHandler( Dart_LibraryTagHandler handler) { Isolate* isolate = Isolate::Current(); CHECK_ISOLATE(isolate); isolate->set_library_tag_handler(handler); return Api::Success(isolate); } // NOTE: Need to pass 'result' as a parameter here in order to avoid // warning: variable 'result' might be clobbered by 'longjmp' or 'vfork' // which shows up because of the use of setjmp. static void CompileSource(Isolate* isolate, const Library& lib, const Script& script, Dart_Handle* result) { bool update_lib_status = (script.kind() == RawScript::kScriptTag || script.kind() == RawScript::kLibraryTag); if (update_lib_status) { lib.SetLoadInProgress(); } ASSERT(isolate != NULL); const Error& error = Error::Handle(isolate, Compiler::Compile(lib, script)); if (error.IsNull()) { *result = Api::NewHandle(isolate, lib.raw()); if (update_lib_status) { lib.SetLoaded(); } } else { *result = Api::NewHandle(isolate, error.raw()); if (update_lib_status) { lib.SetLoadError(); } } } DART_EXPORT Dart_Handle Dart_LoadScript(Dart_Handle url, Dart_Handle source) { TIMERSCOPE(time_script_loading); Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const String& url_str = Api::UnwrapStringHandle(isolate, url); if (url_str.IsNull()) { RETURN_TYPE_ERROR(isolate, url, String); } const String& source_str = Api::UnwrapStringHandle(isolate, source); if (source_str.IsNull()) { RETURN_TYPE_ERROR(isolate, source, String); } Library& library = Library::Handle(isolate, isolate->object_store()->root_library()); if (!library.IsNull()) { const String& library_url = String::Handle(isolate, library.url()); return Api::NewError("%s: A script has already been loaded from '%s'.", CURRENT_FUNC, library_url.ToCString()); } CHECK_CALLBACK_STATE(isolate); library = Library::New(url_str); library.set_debuggable(true); library.Register(); isolate->object_store()->set_root_library(library); const Script& script = Script::Handle( isolate, Script::New(url_str, source_str, RawScript::kScriptTag)); Dart_Handle result; CompileSource(isolate, library, script, &result); return result; } DART_EXPORT Dart_Handle Dart_LoadEmbeddedScript(Dart_Handle url, Dart_Handle source, intptr_t line_offset, intptr_t col_offset) { TIMERSCOPE(time_script_loading); Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const String& url_str = Api::UnwrapStringHandle(isolate, url); if (url_str.IsNull()) { RETURN_TYPE_ERROR(isolate, url, String); } const String& source_str = Api::UnwrapStringHandle(isolate, source); if (source_str.IsNull()) { RETURN_TYPE_ERROR(isolate, source, String); } Library& library = Library::Handle(isolate, isolate->object_store()->root_library()); if (!library.IsNull()) { const String& library_url = String::Handle(isolate, library.url()); return Api::NewError("%s: A script has already been loaded from '%s'.", CURRENT_FUNC, library_url.ToCString()); } if (line_offset < 0) { return Api::NewError("%s: argument 'line_offset' must be positive number", CURRENT_FUNC); } if (col_offset < 0) { return Api::NewError("%s: argument 'col_offset' must be positive number", CURRENT_FUNC); } CHECK_CALLBACK_STATE(isolate); library = Library::New(url_str); library.set_debuggable(true); library.Register(); isolate->object_store()->set_root_library(library); const Script& script = Script::Handle( isolate, Script::New(url_str, source_str, RawScript::kScriptTag)); script.SetLocationOffset(line_offset, col_offset); Dart_Handle result; CompileSource(isolate, library, script, &result); return result; } DART_EXPORT Dart_Handle Dart_LoadScriptFromSnapshot(const uint8_t* buffer) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); TIMERSCOPE(time_script_loading); if (buffer == NULL) { RETURN_NULL_ERROR(buffer); } const Snapshot* snapshot = Snapshot::SetupFromBuffer(buffer); if (!snapshot->IsScriptSnapshot()) { return Api::NewError("%s expects parameter 'buffer' to be a script type" " snapshot.", CURRENT_FUNC); } Library& library = Library::Handle(isolate, isolate->object_store()->root_library()); if (!library.IsNull()) { const String& library_url = String::Handle(isolate, library.url()); return Api::NewError("%s: A script has already been loaded from '%s'.", CURRENT_FUNC, library_url.ToCString()); } CHECK_CALLBACK_STATE(isolate); SnapshotReader reader(snapshot->content(), snapshot->length(), snapshot->kind(), isolate); const Object& tmp = Object::Handle(isolate, reader.ReadObject()); if (!tmp.IsLibrary()) { return Api::NewError("%s: Unable to deserialize snapshot correctly.", CURRENT_FUNC); } library ^= tmp.raw(); library.set_debuggable(true); isolate->object_store()->set_root_library(library); return Api::NewHandle(isolate, library.raw()); } DART_EXPORT Dart_Handle Dart_RootLibrary() { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); Library& library = Library::Handle(isolate, isolate->object_store()->root_library()); return Api::NewHandle(isolate, library.raw()); } static void CompileAll(Isolate* isolate, Dart_Handle* result) { ASSERT(isolate != NULL); const Error& error = Error::Handle(isolate, Library::CompileAll()); if (error.IsNull()) { *result = Api::Success(isolate); } else { *result = Api::NewHandle(isolate, error.raw()); } } DART_EXPORT Dart_Handle Dart_CompileAll() { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); Dart_Handle result = Api::CheckIsolateState(isolate); if (::Dart_IsError(result)) { return result; } CHECK_CALLBACK_STATE(isolate); CompileAll(isolate, &result); return result; } DART_EXPORT bool Dart_IsLibrary(Dart_Handle object) { return Api::ClassId(object) == kLibraryCid; } DART_EXPORT Dart_Handle Dart_GetClass(Dart_Handle library, Dart_Handle class_name) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Library& lib = Api::UnwrapLibraryHandle(isolate, library); if (lib.IsNull()) { RETURN_TYPE_ERROR(isolate, library, Library); } const String& cls_name = Api::UnwrapStringHandle(isolate, class_name); if (cls_name.IsNull()) { RETURN_TYPE_ERROR(isolate, class_name, String); } const Class& cls = Class::Handle(isolate, lib.LookupClassAllowPrivate(cls_name)); if (cls.IsNull()) { // TODO(turnidge): Return null or error in this case? const String& lib_name = String::Handle(isolate, lib.name()); return Api::NewError("Class '%s' not found in library '%s'.", cls_name.ToCString(), lib_name.ToCString()); } return Api::NewHandle(isolate, cls.raw()); } DART_EXPORT Dart_Handle Dart_LibraryName(Dart_Handle library) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Library& lib = Api::UnwrapLibraryHandle(isolate, library); if (lib.IsNull()) { RETURN_TYPE_ERROR(isolate, library, Library); } const String& name = String::Handle(isolate, lib.name()); ASSERT(!name.IsNull()); return Api::NewHandle(isolate, name.raw()); } DART_EXPORT Dart_Handle Dart_LibraryUrl(Dart_Handle library) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Library& lib = Api::UnwrapLibraryHandle(isolate, library); if (lib.IsNull()) { RETURN_TYPE_ERROR(isolate, library, Library); } const String& url = String::Handle(isolate, lib.url()); ASSERT(!url.IsNull()); return Api::NewHandle(isolate, url.raw()); } DART_EXPORT Dart_Handle Dart_LibraryGetClassNames(Dart_Handle library) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Library& lib = Api::UnwrapLibraryHandle(isolate, library); if (lib.IsNull()) { RETURN_TYPE_ERROR(isolate, library, Library); } const GrowableObjectArray& names = GrowableObjectArray::Handle(isolate, GrowableObjectArray::New()); ClassDictionaryIterator it(lib); Class& cls = Class::Handle(); String& name = String::Handle(); while (it.HasNext()) { cls = it.GetNextClass(); if (cls.IsSignatureClass()) { if (!cls.IsCanonicalSignatureClass()) { // This is a typedef. Add it to the list of class names. name = cls.UserVisibleName(); names.Add(name); } else { // Skip canonical signature classes. These are not named. } } else { name = cls.UserVisibleName(); names.Add(name); } } return Api::NewHandle(isolate, Array::MakeArray(names)); } DART_EXPORT Dart_Handle Dart_LookupLibrary(Dart_Handle url) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const String& url_str = Api::UnwrapStringHandle(isolate, url); if (url_str.IsNull()) { RETURN_TYPE_ERROR(isolate, url, String); } const Library& library = Library::Handle(isolate, Library::LookupLibrary(url_str)); if (library.IsNull()) { return Api::NewError("%s: library '%s' not found.", CURRENT_FUNC, url_str.ToCString()); } else { return Api::NewHandle(isolate, library.raw()); } } DART_EXPORT Dart_Handle Dart_LoadLibrary(Dart_Handle url, Dart_Handle source) { TIMERSCOPE(time_script_loading); Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const String& url_str = Api::UnwrapStringHandle(isolate, url); if (url_str.IsNull()) { RETURN_TYPE_ERROR(isolate, url, String); } const String& source_str = Api::UnwrapStringHandle(isolate, source); if (source_str.IsNull()) { RETURN_TYPE_ERROR(isolate, source, String); } CHECK_CALLBACK_STATE(isolate); Library& library = Library::Handle(isolate, Library::LookupLibrary(url_str)); if (library.IsNull()) { library = Library::New(url_str); library.Register(); } else if (!library.LoadNotStarted()) { // The source for this library has either been loaded or is in the // process of loading. Return an error. return Api::NewError("%s: library '%s' has already been loaded.", CURRENT_FUNC, url_str.ToCString()); } const Script& script = Script::Handle( isolate, Script::New(url_str, source_str, RawScript::kLibraryTag)); Dart_Handle result; CompileSource(isolate, library, script, &result); // Propagate the error out right now. if (::Dart_IsError(result)) { return result; } // If this is the dart:builtin library, register it with the VM. if (url_str.Equals("dart:builtin")) { isolate->object_store()->set_builtin_library(library); Dart_Handle state = Api::CheckIsolateState(isolate); if (::Dart_IsError(state)) { return state; } } return result; } DART_EXPORT Dart_Handle Dart_LibraryImportLibrary(Dart_Handle library, Dart_Handle import, Dart_Handle prefix) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Library& library_vm = Api::UnwrapLibraryHandle(isolate, library); if (library_vm.IsNull()) { RETURN_TYPE_ERROR(isolate, library, Library); } const Library& import_vm = Api::UnwrapLibraryHandle(isolate, import); if (import_vm.IsNull()) { RETURN_TYPE_ERROR(isolate, import, Library); } const Object& prefix_object = Object::Handle(isolate, Api::UnwrapHandle(prefix)); const String& prefix_vm = prefix_object.IsNull() ? Symbols::Empty() : String::Cast(prefix_object); if (prefix_vm.IsNull()) { RETURN_TYPE_ERROR(isolate, prefix, String); } CHECK_CALLBACK_STATE(isolate); const String& prefix_symbol = String::Handle(isolate, Symbols::New(prefix_vm)); const Namespace& import_ns = Namespace::Handle( Namespace::New(import_vm, Array::Handle(), Array::Handle())); if (prefix_vm.Length() == 0) { library_vm.AddImport(import_ns); } else { LibraryPrefix& library_prefix = LibraryPrefix::Handle(); library_prefix = library_vm.LookupLocalLibraryPrefix(prefix_symbol); if (!library_prefix.IsNull()) { library_prefix.AddImport(import_ns); } else { library_prefix = LibraryPrefix::New(prefix_symbol, import_ns); library_vm.AddObject(library_prefix, prefix_symbol); } } return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_LoadSource(Dart_Handle library, Dart_Handle url, Dart_Handle source) { TIMERSCOPE(time_script_loading); Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Library& lib = Api::UnwrapLibraryHandle(isolate, library); if (lib.IsNull()) { RETURN_TYPE_ERROR(isolate, library, Library); } const String& url_str = Api::UnwrapStringHandle(isolate, url); if (url_str.IsNull()) { RETURN_TYPE_ERROR(isolate, url, String); } const String& source_str = Api::UnwrapStringHandle(isolate, source); if (source_str.IsNull()) { RETURN_TYPE_ERROR(isolate, source, String); } CHECK_CALLBACK_STATE(isolate); const Script& script = Script::Handle( isolate, Script::New(url_str, source_str, RawScript::kSourceTag)); Dart_Handle result; CompileSource(isolate, lib, script, &result); return result; } DART_EXPORT Dart_Handle Dart_LoadPatch(Dart_Handle library, Dart_Handle url, Dart_Handle patch_source) { TIMERSCOPE(time_script_loading); Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Library& lib = Api::UnwrapLibraryHandle(isolate, library); if (lib.IsNull()) { RETURN_TYPE_ERROR(isolate, library, Library); } const String& url_str = Api::UnwrapStringHandle(isolate, url); if (url_str.IsNull()) { RETURN_TYPE_ERROR(isolate, url, String); } const String& source_str = Api::UnwrapStringHandle(isolate, patch_source); if (source_str.IsNull()) { RETURN_TYPE_ERROR(isolate, patch_source, String); } CHECK_CALLBACK_STATE(isolate); const Script& script = Script::Handle( isolate, Script::New(url_str, source_str, RawScript::kPatchTag)); Dart_Handle result; CompileSource(isolate, lib, script, &result); return result; } DART_EXPORT Dart_Handle Dart_SetNativeResolver( Dart_Handle library, Dart_NativeEntryResolver resolver) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Library& lib = Api::UnwrapLibraryHandle(isolate, library); if (lib.IsNull()) { RETURN_TYPE_ERROR(isolate, library, Library); } lib.set_native_entry_resolver(resolver); return Api::Success(isolate); } // --- Profiling support --- // TODO(7565): Dartium should use the new VM flag "generate_pprof_symbols" for // pprof profiling. Then these symbols should be removed. DART_EXPORT void Dart_InitPprofSupport() { } DART_EXPORT void Dart_GetPprofSymbolInfo(void** buffer, int* buffer_size) { *buffer = NULL; *buffer_size = 0; } // --- Peer support --- DART_EXPORT Dart_Handle Dart_GetPeer(Dart_Handle object, void** peer) { if (peer == NULL) { RETURN_NULL_ERROR(peer); } Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(object)); if (obj.IsNull() || obj.IsNumber() || obj.IsBool()) { const char* msg = "%s: argument 'object' cannot be a subtype of Null, num, or bool"; return Api::NewError(msg, CURRENT_FUNC); } { NoGCScope no_gc; RawObject* raw_obj = obj.raw(); *peer = isolate->heap()->GetPeer(raw_obj); } return Api::Success(isolate); } DART_EXPORT Dart_Handle Dart_SetPeer(Dart_Handle object, void* peer) { Isolate* isolate = Isolate::Current(); DARTSCOPE(isolate); const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(object)); if (obj.IsNull() || obj.IsNumber() || obj.IsBool()) { const char* msg = "%s: argument 'object' cannot be a subtype of Null, num, or bool"; return Api::NewError(msg, CURRENT_FUNC); } { NoGCScope no_gc; RawObject* raw_obj = obj.raw(); isolate->heap()->SetPeer(raw_obj, peer); } return Api::Success(isolate); } } // namespace dart