mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
![Ryan Macnak](/assets/img/avatar_default.png)
Related to https://github.com/dart-lang/sdk/issues/47574 TEST=ci Change-Id: I2f07be6150b025a301e6e4d10935b606087cdf00 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/252462 Reviewed-by: Siva Annamalai <asiva@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
7066 lines
246 KiB
C++
7066 lines
246 KiB
C++
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "include/dart_api.h"
|
|
#include "include/dart_native_api.h"
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "lib/stacktrace.h"
|
|
#include "platform/assert.h"
|
|
#include "platform/unicode.h"
|
|
#include "vm/app_snapshot.h"
|
|
#include "vm/class_finalizer.h"
|
|
#include "vm/compiler/jit/compiler.h"
|
|
#include "vm/dart.h"
|
|
#include "vm/dart_api_impl.h"
|
|
#include "vm/dart_api_message.h"
|
|
#include "vm/dart_api_state.h"
|
|
#include "vm/dart_entry.h"
|
|
#include "vm/debugger.h"
|
|
#include "vm/dwarf.h"
|
|
#include "vm/elf.h"
|
|
#include "vm/exceptions.h"
|
|
#include "vm/flags.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/heap/verifier.h"
|
|
#include "vm/image_snapshot.h"
|
|
#include "vm/isolate_reload.h"
|
|
#include "vm/kernel_isolate.h"
|
|
#include "vm/lockers.h"
|
|
#include "vm/message.h"
|
|
#include "vm/message_handler.h"
|
|
#include "vm/message_snapshot.h"
|
|
#include "vm/native_entry.h"
|
|
#include "vm/native_symbol.h"
|
|
#include "vm/object.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/os.h"
|
|
#include "vm/os_thread.h"
|
|
#include "vm/port.h"
|
|
#include "vm/profiler.h"
|
|
#include "vm/profiler_service.h"
|
|
#include "vm/program_visitor.h"
|
|
#include "vm/resolver.h"
|
|
#include "vm/reusable_handles.h"
|
|
#include "vm/service.h"
|
|
#include "vm/service_event.h"
|
|
#include "vm/service_isolate.h"
|
|
#include "vm/stack_frame.h"
|
|
#include "vm/symbols.h"
|
|
#include "vm/tags.h"
|
|
#include "vm/thread_registry.h"
|
|
#include "vm/uri.h"
|
|
#include "vm/version.h"
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
#include "vm/compiler/aot/precompiler.h"
|
|
#include "vm/kernel_loader.h"
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
namespace dart {
|
|
|
|
// Facilitate quick access to the current zone once we have the current thread.
|
|
#define Z (T->zone())
|
|
|
|
DECLARE_FLAG(bool, print_class_table);
|
|
#if defined(DEBUG) && !defined(DART_PRECOMPILED_RUNTIME)
|
|
DEFINE_FLAG(bool,
|
|
check_function_fingerprints,
|
|
true,
|
|
"Check function fingerprints");
|
|
#endif // defined(DEBUG) && !defined(DART_PRECOMPILED_RUNTIME).
|
|
DEFINE_FLAG(bool,
|
|
verify_acquired_data,
|
|
false,
|
|
"Verify correct API acquire/release of typed data.");
|
|
DEFINE_FLAG(bool,
|
|
dump_tables,
|
|
false,
|
|
"Dump common hash tables before snapshotting.");
|
|
|
|
#define CHECK_ERROR_HANDLE(error) \
|
|
{ \
|
|
ErrorPtr err = (error); \
|
|
if (err != Error::null()) { \
|
|
return Api::NewHandle(T, err); \
|
|
} \
|
|
}
|
|
|
|
ThreadLocalKey Api::api_native_key_ = kUnsetThreadLocalKey;
|
|
Dart_Handle Api::true_handle_ = NULL;
|
|
Dart_Handle Api::false_handle_ = NULL;
|
|
Dart_Handle Api::null_handle_ = NULL;
|
|
Dart_Handle Api::empty_string_handle_ = NULL;
|
|
Dart_Handle Api::no_callbacks_error_handle_ = NULL;
|
|
Dart_Handle Api::unwind_in_progress_error_handle_ = NULL;
|
|
|
|
const char* CanonicalFunction(const char* func) {
|
|
if (strncmp(func, "dart::", 6) == 0) {
|
|
return func + 6;
|
|
} else {
|
|
return func;
|
|
}
|
|
}
|
|
|
|
#if defined(DEBUG)
|
|
// An object visitor which will iterate over all the function objects in the
|
|
// heap and check if the result type and parameter types are canonicalized
|
|
// or not. An assertion is raised if a type is not canonicalized.
|
|
class CheckFunctionTypesVisitor : public ObjectVisitor {
|
|
public:
|
|
explicit CheckFunctionTypesVisitor(Thread* thread)
|
|
: classHandle_(Class::Handle(thread->zone())),
|
|
funcHandle_(Function::Handle(thread->zone())),
|
|
typeHandle_(AbstractType::Handle(thread->zone())) {}
|
|
|
|
void VisitObject(ObjectPtr obj) {
|
|
if (obj->IsFunction()) {
|
|
funcHandle_ ^= obj;
|
|
classHandle_ ^= funcHandle_.Owner();
|
|
// Verify that the result type of a function is canonical or a
|
|
// TypeParameter.
|
|
typeHandle_ ^= funcHandle_.result_type();
|
|
ASSERT(typeHandle_.IsTypeParameter() || typeHandle_.IsCanonical());
|
|
// Verify that the types in the function signature are all canonical or
|
|
// a TypeParameter.
|
|
const intptr_t num_parameters = funcHandle_.NumParameters();
|
|
for (intptr_t i = 0; i < num_parameters; i++) {
|
|
typeHandle_ = funcHandle_.ParameterTypeAt(i);
|
|
ASSERT(typeHandle_.IsTypeParameter() || typeHandle_.IsCanonical());
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
Class& classHandle_;
|
|
Function& funcHandle_;
|
|
AbstractType& typeHandle_;
|
|
};
|
|
#endif // #if defined(DEBUG).
|
|
|
|
static InstancePtr GetListInstance(Zone* zone, const Object& obj) {
|
|
if (obj.IsInstance()) {
|
|
ObjectStore* object_store = IsolateGroup::Current()->object_store();
|
|
const Type& list_rare_type =
|
|
Type::Handle(zone, object_store->non_nullable_list_rare_type());
|
|
ASSERT(!list_rare_type.IsNull());
|
|
const Instance& instance = Instance::Cast(obj);
|
|
const Class& obj_class = Class::Handle(zone, obj.clazz());
|
|
if (Class::IsSubtypeOf(obj_class, Object::null_type_arguments(),
|
|
Nullability::kNonNullable, list_rare_type,
|
|
Heap::kNew)) {
|
|
return instance.ptr();
|
|
}
|
|
}
|
|
return Instance::null();
|
|
}
|
|
|
|
static InstancePtr GetMapInstance(Zone* zone, const Object& obj) {
|
|
if (obj.IsInstance()) {
|
|
ObjectStore* object_store = IsolateGroup::Current()->object_store();
|
|
const Type& map_rare_type =
|
|
Type::Handle(zone, object_store->non_nullable_map_rare_type());
|
|
ASSERT(!map_rare_type.IsNull());
|
|
const Instance& instance = Instance::Cast(obj);
|
|
const Class& obj_class = Class::Handle(zone, obj.clazz());
|
|
if (Class::IsSubtypeOf(obj_class, Object::null_type_arguments(),
|
|
Nullability::kNonNullable, map_rare_type,
|
|
Heap::kNew)) {
|
|
return instance.ptr();
|
|
}
|
|
}
|
|
return Instance::null();
|
|
}
|
|
|
|
static bool IsCompiletimeErrorObject(Zone* zone, const Object& obj) {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
// All compile-time errors were handled at snapshot generation time and
|
|
// compiletime_error_class was removed.
|
|
return false;
|
|
#else
|
|
auto isolate_group = Thread::Current()->isolate_group();
|
|
const Class& error_class = Class::Handle(
|
|
zone, isolate_group->object_store()->compiletime_error_class());
|
|
ASSERT(!error_class.IsNull());
|
|
return (obj.GetClassId() == error_class.id());
|
|
#endif
|
|
}
|
|
|
|
static bool GetNativeStringArgument(NativeArguments* arguments,
|
|
int arg_index,
|
|
Dart_Handle* str,
|
|
void** peer) {
|
|
ASSERT(peer != NULL);
|
|
if (Api::StringGetPeerHelper(arguments, arg_index, peer)) {
|
|
*str = NULL;
|
|
return true;
|
|
}
|
|
Thread* thread = arguments->thread();
|
|
ASSERT(thread == Thread::Current());
|
|
*peer = NULL;
|
|
REUSABLE_OBJECT_HANDLESCOPE(thread);
|
|
Object& obj = thread->ObjectHandle();
|
|
obj = arguments->NativeArgAt(arg_index);
|
|
if (IsStringClassId(obj.GetClassId())) {
|
|
ASSERT(thread->api_top_scope() != NULL);
|
|
*str = Api::NewHandle(thread, obj.ptr());
|
|
return true;
|
|
}
|
|
if (obj.IsNull()) {
|
|
*str = Api::Null();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool GetNativeIntegerArgument(NativeArguments* arguments,
|
|
int arg_index,
|
|
int64_t* value) {
|
|
ASSERT(value != NULL);
|
|
return Api::GetNativeIntegerArgument(arguments, arg_index, value);
|
|
}
|
|
|
|
static bool GetNativeUnsignedIntegerArgument(NativeArguments* arguments,
|
|
int arg_index,
|
|
uint64_t* value) {
|
|
ASSERT(value != NULL);
|
|
int64_t arg_value = 0;
|
|
if (Api::GetNativeIntegerArgument(arguments, arg_index, &arg_value)) {
|
|
*value = static_cast<uint64_t>(arg_value);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool GetNativeDoubleArgument(NativeArguments* arguments,
|
|
int arg_index,
|
|
double* value) {
|
|
ASSERT(value != NULL);
|
|
return Api::GetNativeDoubleArgument(arguments, arg_index, value);
|
|
}
|
|
|
|
static Dart_Handle GetNativeFieldsOfArgument(NativeArguments* arguments,
|
|
int arg_index,
|
|
int num_fields,
|
|
intptr_t* field_values,
|
|
const char* current_func) {
|
|
ASSERT(field_values != NULL);
|
|
if (Api::GetNativeFieldsOfArgument(arguments, arg_index, num_fields,
|
|
field_values)) {
|
|
return Api::Success();
|
|
}
|
|
Thread* thread = arguments->thread();
|
|
ASSERT(thread == Thread::Current());
|
|
REUSABLE_OBJECT_HANDLESCOPE(thread);
|
|
Object& obj = thread->ObjectHandle();
|
|
obj = arguments->NativeArgAt(arg_index);
|
|
if (obj.IsNull()) {
|
|
memset(field_values, 0, (num_fields * sizeof(field_values[0])));
|
|
return Api::Success();
|
|
}
|
|
// We did not succeed in extracting the native fields report the
|
|
// appropriate error.
|
|
if (!obj.IsInstance()) {
|
|
return Api::NewError(
|
|
"%s expects argument at index '%d' to be of"
|
|
" type Instance.",
|
|
current_func, arg_index);
|
|
}
|
|
const Instance& instance = Instance::Cast(obj);
|
|
int field_count = instance.NumNativeFields();
|
|
ASSERT(num_fields != field_count);
|
|
return Api::NewError("%s: expected %d 'num_fields' but was passed in %d.",
|
|
current_func, field_count, num_fields);
|
|
}
|
|
|
|
static FunctionPtr FindCoreLibPrivateFunction(Zone* zone, const String& name) {
|
|
const Library& core_lib = Library::Handle(zone, Library::CoreLibrary());
|
|
ASSERT(!core_lib.IsNull());
|
|
const Function& function =
|
|
Function::Handle(zone, core_lib.LookupFunctionAllowPrivate(name));
|
|
ASSERT(!function.IsNull());
|
|
return function.ptr();
|
|
}
|
|
|
|
static ObjectPtr CallStatic1Arg(Zone* zone,
|
|
const String& name,
|
|
const Instance& arg0) {
|
|
const intptr_t kNumArgs = 1;
|
|
const Function& function =
|
|
Function::Handle(zone, FindCoreLibPrivateFunction(zone, name));
|
|
const Array& args = Array::Handle(zone, Array::New(kNumArgs));
|
|
args.SetAt(0, arg0);
|
|
return DartEntry::InvokeFunction(function, args);
|
|
}
|
|
|
|
static ObjectPtr CallStatic2Args(Zone* zone,
|
|
const String& name,
|
|
const Instance& arg0,
|
|
const Instance& arg1) {
|
|
const intptr_t kNumArgs = 2;
|
|
const Function& function =
|
|
Function::Handle(zone, FindCoreLibPrivateFunction(zone, name));
|
|
const Array& args = Array::Handle(zone, Array::New(kNumArgs));
|
|
args.SetAt(0, arg0);
|
|
args.SetAt(1, arg1);
|
|
return DartEntry::InvokeFunction(function, args);
|
|
}
|
|
|
|
static ObjectPtr CallStatic3Args(Zone* zone,
|
|
const String& name,
|
|
const Instance& arg0,
|
|
const Instance& arg1,
|
|
const Instance& arg2) {
|
|
const intptr_t kNumArgs = 3;
|
|
const Function& function =
|
|
Function::Handle(zone, FindCoreLibPrivateFunction(zone, name));
|
|
const Array& args = Array::Handle(Array::New(kNumArgs));
|
|
args.SetAt(0, arg0);
|
|
args.SetAt(1, arg1);
|
|
args.SetAt(2, arg2);
|
|
return DartEntry::InvokeFunction(function, args);
|
|
}
|
|
|
|
static const char* GetErrorString(Thread* thread, const Object& obj) {
|
|
// This function requires an API scope to be present.
|
|
if (obj.IsError()) {
|
|
ASSERT(thread->api_top_scope() != NULL);
|
|
const Error& error = Error::Cast(obj);
|
|
const char* str = error.ToErrorCString();
|
|
intptr_t len = strlen(str) + 1;
|
|
char* str_copy = Api::TopScope(thread)->zone()->Alloc<char>(len);
|
|
strncpy(str_copy, str, len);
|
|
// Strip a possible trailing '\n'.
|
|
if ((len > 1) && (str_copy[len - 2] == '\n')) {
|
|
str_copy[len - 2] = '\0';
|
|
}
|
|
return str_copy;
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
Dart_Handle Api::InitNewHandle(Thread* thread, ObjectPtr raw) {
|
|
LocalHandles* local_handles = Api::TopScope(thread)->local_handles();
|
|
ASSERT(local_handles != NULL);
|
|
LocalHandle* ref = local_handles->AllocateHandle();
|
|
ref->set_ptr(raw);
|
|
return ref->apiHandle();
|
|
}
|
|
|
|
Dart_Handle Api::NewHandle(Thread* thread, ObjectPtr raw) {
|
|
if (raw == Object::null()) {
|
|
return Null();
|
|
}
|
|
if (raw == Bool::True().ptr()) {
|
|
return True();
|
|
}
|
|
if (raw == Bool::False().ptr()) {
|
|
return False();
|
|
}
|
|
ASSERT(thread->execution_state() == Thread::kThreadInVM);
|
|
return InitNewHandle(thread, raw);
|
|
}
|
|
|
|
ObjectPtr Api::UnwrapHandle(Dart_Handle object) {
|
|
#if defined(DEBUG)
|
|
Thread* thread = Thread::Current();
|
|
ASSERT(thread->execution_state() == Thread::kThreadInVM);
|
|
ASSERT(thread->IsMutatorThread());
|
|
ASSERT(thread->isolate() != NULL);
|
|
ASSERT(FinalizablePersistentHandle::ptr_offset() == 0 &&
|
|
PersistentHandle::ptr_offset() == 0 && LocalHandle::ptr_offset() == 0);
|
|
#endif
|
|
return (reinterpret_cast<LocalHandle*>(object))->ptr();
|
|
}
|
|
|
|
#define DEFINE_UNWRAP(type) \
|
|
const type& Api::Unwrap##type##Handle(Zone* zone, Dart_Handle dart_handle) { \
|
|
const Object& obj = Object::Handle(zone, Api::UnwrapHandle(dart_handle)); \
|
|
if (obj.Is##type()) { \
|
|
return type::Cast(obj); \
|
|
} \
|
|
return type::Handle(zone); \
|
|
}
|
|
CLASS_LIST_FOR_HANDLES(DEFINE_UNWRAP)
|
|
#undef DEFINE_UNWRAP
|
|
|
|
const String& Api::UnwrapStringHandle(const ReusableObjectHandleScope& reuse,
|
|
Dart_Handle dart_handle) {
|
|
Object& ref = reuse.Handle();
|
|
ref = Api::UnwrapHandle(dart_handle);
|
|
if (ref.IsString()) {
|
|
return String::Cast(ref);
|
|
}
|
|
return Object::null_string();
|
|
}
|
|
|
|
const Instance& Api::UnwrapInstanceHandle(
|
|
const ReusableObjectHandleScope& reuse,
|
|
Dart_Handle dart_handle) {
|
|
Object& ref = reuse.Handle();
|
|
ref = Api::UnwrapHandle(dart_handle);
|
|
if (ref.IsInstance()) {
|
|
return Instance::Cast(ref);
|
|
}
|
|
return Object::null_instance();
|
|
}
|
|
|
|
Dart_Handle Api::CheckAndFinalizePendingClasses(Thread* thread) {
|
|
Isolate* isolate = thread->isolate();
|
|
if (!isolate->AllowClassFinalization()) {
|
|
// Class finalization is blocked for the isolate. Do nothing.
|
|
return Api::Success();
|
|
}
|
|
if (ClassFinalizer::ProcessPendingClasses()) {
|
|
return Api::Success();
|
|
}
|
|
ASSERT(thread->sticky_error() != Object::null());
|
|
return Api::NewHandle(thread, thread->sticky_error());
|
|
}
|
|
|
|
Dart_Isolate Api::CastIsolate(Isolate* isolate) {
|
|
return reinterpret_cast<Dart_Isolate>(isolate);
|
|
}
|
|
|
|
Dart_IsolateGroup Api::CastIsolateGroup(IsolateGroup* isolate_group) {
|
|
return reinterpret_cast<Dart_IsolateGroup>(isolate_group);
|
|
}
|
|
|
|
Dart_Handle Api::NewError(const char* format, ...) {
|
|
Thread* T = Thread::Current();
|
|
CHECK_API_SCOPE(T);
|
|
CHECK_CALLBACK_STATE(T);
|
|
// Ensure we transition safepoint state to VM if we are not already in
|
|
// that state.
|
|
TransitionToVM transition(T);
|
|
HANDLESCOPE(T);
|
|
|
|
va_list args;
|
|
va_start(args, format);
|
|
char* buffer = OS::VSCreate(Z, format, args);
|
|
va_end(args);
|
|
|
|
const String& message = String::Handle(Z, String::New(buffer));
|
|
return Api::NewHandle(T, ApiError::New(message));
|
|
}
|
|
|
|
Dart_Handle Api::NewArgumentError(const char* format, ...) {
|
|
Thread* T = Thread::Current();
|
|
CHECK_API_SCOPE(T);
|
|
CHECK_CALLBACK_STATE(T);
|
|
// Ensure we transition safepoint state to VM if we are not already in
|
|
// that state.
|
|
TransitionToVM transition(T);
|
|
HANDLESCOPE(T);
|
|
|
|
va_list args;
|
|
va_start(args, format);
|
|
char* buffer = OS::VSCreate(Z, format, args);
|
|
va_end(args);
|
|
|
|
const String& message = String::Handle(Z, String::New(buffer));
|
|
const Array& arguments = Array::Handle(Z, Array::New(1));
|
|
arguments.SetAt(0, message);
|
|
Object& error = Object::Handle(
|
|
Z, DartLibraryCalls::InstanceCreate(
|
|
Library::Handle(Z, Library::CoreLibrary()),
|
|
Symbols::ArgumentError(), Symbols::Dot(), arguments));
|
|
if (!error.IsError()) {
|
|
error = UnhandledException::New(Instance::Cast(error), Instance::Handle());
|
|
}
|
|
return Api::NewHandle(T, error.ptr());
|
|
}
|
|
|
|
bool Api::IsValid(Dart_Handle handle) {
|
|
Isolate* isolate = Isolate::Current();
|
|
Thread* thread = Thread::Current();
|
|
ASSERT(thread->IsMutatorThread());
|
|
CHECK_ISOLATE(isolate);
|
|
|
|
// Check against all of the handles in the current isolate as well as the
|
|
// read-only handles.
|
|
return thread->IsValidHandle(handle) ||
|
|
isolate->group()->api_state()->IsActivePersistentHandle(
|
|
reinterpret_cast<Dart_PersistentHandle>(handle)) ||
|
|
isolate->group()->api_state()->IsActiveWeakPersistentHandle(
|
|
reinterpret_cast<Dart_WeakPersistentHandle>(handle)) ||
|
|
Dart::IsReadOnlyApiHandle(handle) ||
|
|
Dart::IsReadOnlyHandle(reinterpret_cast<uword>(handle));
|
|
}
|
|
|
|
ApiLocalScope* Api::TopScope(Thread* thread) {
|
|
ASSERT(thread != NULL);
|
|
ApiLocalScope* scope = thread->api_top_scope();
|
|
ASSERT(scope != NULL);
|
|
return scope;
|
|
}
|
|
|
|
void Api::Init() {
|
|
if (api_native_key_ == kUnsetThreadLocalKey) {
|
|
api_native_key_ = OSThread::CreateThreadLocal();
|
|
}
|
|
ASSERT(api_native_key_ != kUnsetThreadLocalKey);
|
|
}
|
|
|
|
static Dart_Handle InitNewReadOnlyApiHandle(ObjectPtr raw) {
|
|
ASSERT(raw->untag()->InVMIsolateHeap());
|
|
LocalHandle* ref = Dart::AllocateReadOnlyApiHandle();
|
|
ref->set_ptr(raw);
|
|
return ref->apiHandle();
|
|
}
|
|
|
|
void Api::InitHandles() {
|
|
Isolate* isolate = Isolate::Current();
|
|
ASSERT(isolate != NULL);
|
|
ASSERT(isolate == Dart::vm_isolate());
|
|
ApiState* state = isolate->group()->api_state();
|
|
ASSERT(state != NULL);
|
|
|
|
ASSERT(true_handle_ == NULL);
|
|
true_handle_ = InitNewReadOnlyApiHandle(Bool::True().ptr());
|
|
|
|
ASSERT(false_handle_ == NULL);
|
|
false_handle_ = InitNewReadOnlyApiHandle(Bool::False().ptr());
|
|
|
|
ASSERT(null_handle_ == NULL);
|
|
null_handle_ = InitNewReadOnlyApiHandle(Object::null());
|
|
|
|
ASSERT(empty_string_handle_ == NULL);
|
|
empty_string_handle_ = InitNewReadOnlyApiHandle(Symbols::Empty().ptr());
|
|
|
|
ASSERT(no_callbacks_error_handle_ == NULL);
|
|
no_callbacks_error_handle_ =
|
|
InitNewReadOnlyApiHandle(Object::no_callbacks_error().ptr());
|
|
|
|
ASSERT(unwind_in_progress_error_handle_ == NULL);
|
|
unwind_in_progress_error_handle_ =
|
|
InitNewReadOnlyApiHandle(Object::unwind_in_progress_error().ptr());
|
|
}
|
|
|
|
void Api::Cleanup() {
|
|
true_handle_ = NULL;
|
|
false_handle_ = NULL;
|
|
null_handle_ = NULL;
|
|
empty_string_handle_ = NULL;
|
|
no_callbacks_error_handle_ = NULL;
|
|
unwind_in_progress_error_handle_ = NULL;
|
|
}
|
|
|
|
bool Api::StringGetPeerHelper(NativeArguments* arguments,
|
|
int arg_index,
|
|
void** peer) {
|
|
NoSafepointScope no_safepoint_scope;
|
|
ObjectPtr raw_obj = arguments->NativeArgAt(arg_index);
|
|
if (!raw_obj->IsHeapObject()) {
|
|
return false;
|
|
}
|
|
intptr_t cid = raw_obj->GetClassId();
|
|
if (cid == kExternalOneByteStringCid) {
|
|
ExternalOneByteStringPtr raw_string =
|
|
static_cast<ExternalOneByteStringPtr>(raw_obj);
|
|
*peer = raw_string->untag()->peer_;
|
|
return true;
|
|
}
|
|
if (cid == kOneByteStringCid || cid == kTwoByteStringCid) {
|
|
auto isolate_group = arguments->thread()->isolate_group();
|
|
*peer = isolate_group->heap()->GetPeer(raw_obj);
|
|
return (*peer != 0);
|
|
}
|
|
if (cid == kExternalTwoByteStringCid) {
|
|
ExternalTwoByteStringPtr raw_string =
|
|
static_cast<ExternalTwoByteStringPtr>(raw_obj);
|
|
*peer = raw_string->untag()->peer_;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Api::GetNativeReceiver(NativeArguments* arguments, intptr_t* value) {
|
|
NoSafepointScope no_safepoint_scope;
|
|
ObjectPtr raw_obj = arguments->NativeArg0();
|
|
if (raw_obj->IsHeapObject()) {
|
|
intptr_t cid = raw_obj->GetClassId();
|
|
if (cid >= kNumPredefinedCids) {
|
|
ASSERT(Instance::Cast(Object::Handle(raw_obj)).IsValidNativeIndex(0));
|
|
TypedDataPtr native_fields =
|
|
reinterpret_cast<CompressedTypedDataPtr*>(
|
|
UntaggedObject::ToAddr(raw_obj) + sizeof(UntaggedObject))
|
|
->Decompress(raw_obj->heap_base());
|
|
if (native_fields == TypedData::null()) {
|
|
*value = 0;
|
|
} else {
|
|
*value = *bit_cast<intptr_t*, uint8_t*>(native_fields->untag()->data());
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Api::GetNativeBooleanArgument(NativeArguments* arguments,
|
|
int arg_index,
|
|
bool* value) {
|
|
NoSafepointScope no_safepoint_scope;
|
|
ObjectPtr raw_obj = arguments->NativeArgAt(arg_index);
|
|
if (raw_obj->IsHeapObject()) {
|
|
intptr_t cid = raw_obj->GetClassId();
|
|
if (cid == kBoolCid) {
|
|
*value = (raw_obj == Object::bool_true().ptr());
|
|
return true;
|
|
}
|
|
if (cid == kNullCid) {
|
|
*value = false;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Api::GetNativeIntegerArgument(NativeArguments* arguments,
|
|
int arg_index,
|
|
int64_t* value) {
|
|
NoSafepointScope no_safepoint_scope;
|
|
ObjectPtr raw_obj = arguments->NativeArgAt(arg_index);
|
|
if (raw_obj->IsHeapObject()) {
|
|
intptr_t cid = raw_obj->GetClassId();
|
|
if (cid == kMintCid) {
|
|
*value = static_cast<MintPtr>(raw_obj)->untag()->value_;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
*value = Smi::Value(static_cast<SmiPtr>(raw_obj));
|
|
return true;
|
|
}
|
|
|
|
bool Api::GetNativeDoubleArgument(NativeArguments* arguments,
|
|
int arg_index,
|
|
double* value) {
|
|
NoSafepointScope no_safepoint_scope;
|
|
ObjectPtr raw_obj = arguments->NativeArgAt(arg_index);
|
|
if (raw_obj->IsHeapObject()) {
|
|
intptr_t cid = raw_obj->GetClassId();
|
|
if (cid == kDoubleCid) {
|
|
*value = static_cast<DoublePtr>(raw_obj)->untag()->value_;
|
|
return true;
|
|
}
|
|
if (cid == kMintCid) {
|
|
*value =
|
|
static_cast<double>(static_cast<MintPtr>(raw_obj)->untag()->value_);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
*value = static_cast<double>(Smi::Value(static_cast<SmiPtr>(raw_obj)));
|
|
return true;
|
|
}
|
|
|
|
bool Api::GetNativeFieldsOfArgument(NativeArguments* arguments,
|
|
int arg_index,
|
|
int num_fields,
|
|
intptr_t* field_values) {
|
|
NoSafepointScope no_safepoint_scope;
|
|
ObjectPtr raw_obj = arguments->NativeArgAt(arg_index);
|
|
intptr_t cid = raw_obj->GetClassIdMayBeSmi();
|
|
int class_num_fields = arguments->thread()
|
|
->isolate_group()
|
|
->class_table()
|
|
->At(cid)
|
|
->untag()
|
|
->num_native_fields_;
|
|
if (num_fields != class_num_fields) {
|
|
// No native fields or mismatched native field count.
|
|
return false;
|
|
}
|
|
TypedDataPtr native_fields =
|
|
reinterpret_cast<CompressedTypedDataPtr*>(
|
|
UntaggedObject::ToAddr(raw_obj) + sizeof(UntaggedObject))
|
|
->Decompress(raw_obj->heap_base());
|
|
if (native_fields == TypedData::null()) {
|
|
// Native fields not initialized.
|
|
memset(field_values, 0, (num_fields * sizeof(field_values[0])));
|
|
return true;
|
|
}
|
|
ASSERT(class_num_fields == Smi::Value(native_fields->untag()->length()));
|
|
intptr_t* native_values =
|
|
reinterpret_cast<intptr_t*>(native_fields->untag()->data());
|
|
memmove(field_values, native_values, (num_fields * sizeof(field_values[0])));
|
|
return true;
|
|
}
|
|
|
|
void Api::SetWeakHandleReturnValue(NativeArguments* args,
|
|
Dart_WeakPersistentHandle retval) {
|
|
args->SetReturnUnsafe(FinalizablePersistentHandle::Cast(retval)->ptr());
|
|
}
|
|
|
|
PersistentHandle* PersistentHandle::Cast(Dart_PersistentHandle handle) {
|
|
ASSERT(IsolateGroup::Current()->api_state()->IsValidPersistentHandle(handle));
|
|
return reinterpret_cast<PersistentHandle*>(handle);
|
|
}
|
|
|
|
FinalizablePersistentHandle* FinalizablePersistentHandle::Cast(
|
|
Dart_WeakPersistentHandle handle) {
|
|
#if defined(DEBUG)
|
|
ApiState* state = IsolateGroup::Current()->api_state();
|
|
ASSERT(state->IsValidWeakPersistentHandle(handle));
|
|
#endif
|
|
return reinterpret_cast<FinalizablePersistentHandle*>(handle);
|
|
}
|
|
FinalizablePersistentHandle* FinalizablePersistentHandle::Cast(
|
|
Dart_FinalizableHandle handle) {
|
|
#if defined(DEBUG)
|
|
ApiState* state = IsolateGroup::Current()->api_state();
|
|
ASSERT(state->IsValidFinalizableHandle(handle));
|
|
#endif
|
|
return reinterpret_cast<FinalizablePersistentHandle*>(handle);
|
|
}
|
|
|
|
void FinalizablePersistentHandle::Finalize(
|
|
IsolateGroup* isolate_group,
|
|
FinalizablePersistentHandle* handle) {
|
|
if (!handle->ptr()->IsHeapObject()) {
|
|
return; // Free handle.
|
|
}
|
|
Dart_HandleFinalizer callback = handle->callback();
|
|
ASSERT(callback != NULL);
|
|
void* peer = handle->peer();
|
|
ApiState* state = isolate_group->api_state();
|
|
ASSERT(state != NULL);
|
|
|
|
if (!handle->auto_delete()) {
|
|
// Clear handle before running finalizer, finalizer can free the handle.
|
|
state->ClearWeakPersistentHandle(handle);
|
|
}
|
|
|
|
(*callback)(isolate_group->embedder_data(), peer);
|
|
|
|
if (handle->auto_delete()) {
|
|
state->FreeWeakPersistentHandle(handle);
|
|
}
|
|
}
|
|
|
|
// --- Handles ---
|
|
|
|
DART_EXPORT bool Dart_IsError(Dart_Handle handle) {
|
|
Thread* thread = Thread::Current();
|
|
TransitionNativeToVM transition(thread);
|
|
return Api::IsError(handle);
|
|
}
|
|
|
|
DART_EXPORT void Dart_KillIsolate(Dart_Isolate handle) {
|
|
Isolate* isolate = reinterpret_cast<Isolate*>(handle);
|
|
CHECK_ISOLATE(isolate);
|
|
Isolate::KillIfExists(isolate, Isolate::kKillMsg);
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsApiError(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
TransitionNativeToVM transition(thread);
|
|
return Api::ClassId(object) == kApiErrorCid;
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsUnhandledExceptionError(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
TransitionNativeToVM transition(thread);
|
|
return Api::ClassId(object) == kUnhandledExceptionCid;
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsCompilationError(Dart_Handle object) {
|
|
if (::Dart_IsUnhandledExceptionError(object)) {
|
|
DARTSCOPE(Thread::Current());
|
|
const UnhandledException& error =
|
|
UnhandledException::Cast(Object::Handle(Z, Api::UnwrapHandle(object)));
|
|
const Instance& exc = Instance::Handle(Z, error.exception());
|
|
return IsCompiletimeErrorObject(Z, exc);
|
|
}
|
|
|
|
Thread* thread = Thread::Current();
|
|
TransitionNativeToVM transition(thread);
|
|
return Api::ClassId(object) == kLanguageErrorCid;
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsFatalError(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
TransitionNativeToVM transition(thread);
|
|
return Api::ClassId(object) == kUnwindErrorCid;
|
|
}
|
|
|
|
DART_EXPORT const char* Dart_GetError(Dart_Handle handle) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(handle));
|
|
return GetErrorString(T, obj);
|
|
}
|
|
|
|
DART_EXPORT bool Dart_ErrorHasException(Dart_Handle handle) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(handle));
|
|
return obj.IsUnhandledException();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_ErrorGetException(Dart_Handle handle) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(handle));
|
|
if (obj.IsUnhandledException()) {
|
|
const UnhandledException& error = UnhandledException::Cast(obj);
|
|
return Api::NewHandle(T, error.exception());
|
|
} else if (obj.IsError()) {
|
|
return Api::NewError("This error is not an unhandled exception error.");
|
|
} else {
|
|
return Api::NewError("Can only get exceptions from error handles.");
|
|
}
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_ErrorGetStackTrace(Dart_Handle handle) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(handle));
|
|
if (obj.IsUnhandledException()) {
|
|
const UnhandledException& error = UnhandledException::Cast(obj);
|
|
return Api::NewHandle(T, error.stacktrace());
|
|
} else if (obj.IsError()) {
|
|
return Api::NewError("This error is not an unhandled exception error.");
|
|
} else {
|
|
return Api::NewError("Can only get stacktraces from error handles.");
|
|
}
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewApiError(const char* error) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
const String& message = String::Handle(Z, String::New(error));
|
|
return Api::NewHandle(T, ApiError::New(message));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewCompilationError(const char* error) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
const String& message = String::Handle(Z, String::New(error));
|
|
return Api::NewHandle(T, LanguageError::New(message));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewUnhandledExceptionError(Dart_Handle exception) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
Instance& obj = Instance::Handle(Z);
|
|
intptr_t class_id = Api::ClassId(exception);
|
|
if ((class_id == kApiErrorCid) || (class_id == kLanguageErrorCid)) {
|
|
const Object& excp = Object::Handle(Z, Api::UnwrapHandle(exception));
|
|
obj = String::New(GetErrorString(T, excp));
|
|
} else {
|
|
obj = Api::UnwrapInstanceHandle(Z, exception).ptr();
|
|
if (obj.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, exception, Instance);
|
|
}
|
|
}
|
|
const StackTrace& stacktrace = StackTrace::Handle(Z);
|
|
return Api::NewHandle(T, UnhandledException::New(obj, stacktrace));
|
|
}
|
|
|
|
DART_EXPORT void Dart_PropagateError(Dart_Handle handle) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
const Object& obj = Object::Handle(thread->zone(), Api::UnwrapHandle(handle));
|
|
if (!obj.IsError()) {
|
|
FATAL1(
|
|
"%s expects argument 'handle' to be an error handle. "
|
|
"Did you forget to check Dart_IsError first?",
|
|
CURRENT_FUNC);
|
|
}
|
|
if (thread->top_exit_frame_info() == 0) {
|
|
// There are no dart frames on the stack so it would be illegal to
|
|
// propagate an error here.
|
|
FATAL("No Dart frames on stack, cannot propagate error.");
|
|
}
|
|
// Unwind all the API scopes till the exit frame before propagating.
|
|
const Error* error;
|
|
{
|
|
// We need to preserve the error object across the destruction of zones
|
|
// when the ApiScopes are unwound. By using NoSafepointScope, we can ensure
|
|
// that GC won't touch the raw error object before creating a valid
|
|
// handle for it in the surviving zone.
|
|
NoSafepointScope no_safepoint;
|
|
ErrorPtr raw_error = Api::UnwrapErrorHandle(thread->zone(), handle).ptr();
|
|
thread->UnwindScopes(thread->top_exit_frame_info());
|
|
// Note that thread's zone is different here than at the beginning of this
|
|
// function.
|
|
error = &Error::Handle(thread->zone(), raw_error);
|
|
}
|
|
Exceptions::PropagateError(*error);
|
|
UNREACHABLE();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_ToString(Dart_Handle object) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(object));
|
|
if (obj.IsString()) {
|
|
return Api::NewHandle(T, obj.ptr());
|
|
} else if (obj.IsInstance()) {
|
|
CHECK_CALLBACK_STATE(T);
|
|
const Instance& receiver = Instance::Cast(obj);
|
|
return Api::NewHandle(T, DartLibraryCalls::ToString(receiver));
|
|
} else {
|
|
CHECK_CALLBACK_STATE(T);
|
|
// This is a VM internal object. Call the C++ method of printing.
|
|
return Api::NewHandle(T, String::New(obj.ToCString()));
|
|
}
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IdentityEquals(Dart_Handle obj1, Dart_Handle obj2) {
|
|
DARTSCOPE(Thread::Current());
|
|
{
|
|
NoSafepointScope no_safepoint_scope;
|
|
if (Api::UnwrapHandle(obj1) == Api::UnwrapHandle(obj2)) {
|
|
return true;
|
|
}
|
|
}
|
|
const Object& object1 = Object::Handle(Z, Api::UnwrapHandle(obj1));
|
|
const Object& object2 = Object::Handle(Z, Api::UnwrapHandle(obj2));
|
|
if (object1.IsInstance() && object2.IsInstance()) {
|
|
return Instance::Cast(object1).IsIdenticalTo(Instance::Cast(object2));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_HandleFromPersistent(Dart_PersistentHandle object) {
|
|
Thread* thread = Thread::Current();
|
|
Isolate* isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
ApiState* state = isolate->group()->api_state();
|
|
ASSERT(state != NULL);
|
|
TransitionNativeToVM transition(thread);
|
|
NoSafepointScope no_safepoint_scope;
|
|
PersistentHandle* ref = PersistentHandle::Cast(object);
|
|
return Api::NewHandle(thread, ref->ptr());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_HandleFromWeakPersistent(Dart_WeakPersistentHandle object) {
|
|
Thread* thread = Thread::Current();
|
|
Isolate* isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
ApiState* state = isolate->group()->api_state();
|
|
ASSERT(state != NULL);
|
|
TransitionNativeToVM transition(thread);
|
|
NoSafepointScope no_safepoint_scope;
|
|
FinalizablePersistentHandle* weak_ref =
|
|
FinalizablePersistentHandle::Cast(object);
|
|
if (weak_ref->IsFinalizedNotFreed()) {
|
|
return Dart_Null();
|
|
}
|
|
return Api::NewHandle(thread, weak_ref->ptr());
|
|
}
|
|
|
|
static Dart_Handle HandleFromFinalizable(Dart_FinalizableHandle object) {
|
|
Thread* thread = Thread::Current();
|
|
Isolate* isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
ApiState* state = isolate->group()->api_state();
|
|
ASSERT(state != NULL);
|
|
TransitionNativeToVM transition(thread);
|
|
NoSafepointScope no_safepoint_scope;
|
|
FinalizablePersistentHandle* weak_ref =
|
|
FinalizablePersistentHandle::Cast(object);
|
|
return Api::NewHandle(thread, weak_ref->ptr());
|
|
}
|
|
|
|
DART_EXPORT Dart_PersistentHandle Dart_NewPersistentHandle(Dart_Handle object) {
|
|
DARTSCOPE(Thread::Current());
|
|
Isolate* I = T->isolate();
|
|
ApiState* state = I->group()->api_state();
|
|
ASSERT(state != NULL);
|
|
const Object& old_ref = Object::Handle(Z, Api::UnwrapHandle(object));
|
|
PersistentHandle* new_ref = state->AllocatePersistentHandle();
|
|
new_ref->set_ptr(old_ref);
|
|
return new_ref->apiHandle();
|
|
}
|
|
|
|
DART_EXPORT void Dart_SetPersistentHandle(Dart_PersistentHandle obj1,
|
|
Dart_Handle obj2) {
|
|
DARTSCOPE(Thread::Current());
|
|
Isolate* I = T->isolate();
|
|
ApiState* state = I->group()->api_state();
|
|
ASSERT(state != NULL);
|
|
ASSERT(state->IsValidPersistentHandle(obj1));
|
|
const Object& obj2_ref = Object::Handle(Z, Api::UnwrapHandle(obj2));
|
|
PersistentHandle* obj1_ref = PersistentHandle::Cast(obj1);
|
|
obj1_ref->set_ptr(obj2_ref);
|
|
}
|
|
|
|
static bool IsFfiCompound(Thread* T, const Object& obj) {
|
|
if (obj.IsNull()) {
|
|
return false;
|
|
}
|
|
|
|
// CFE guarantees we can only have direct subclasses of `Struct` and `Union`
|
|
// (no implementations or indirect subclasses are allowed).
|
|
const auto& klass = Class::Handle(Z, obj.clazz());
|
|
const auto& super_klass = Class::Handle(Z, klass.SuperClass());
|
|
if (super_klass.IsNull()) {
|
|
// This means klass is Object.
|
|
return false;
|
|
}
|
|
if (super_klass.Name() != Symbols::Struct().ptr() &&
|
|
super_klass.Name() != Symbols::Union().ptr()) {
|
|
return false;
|
|
}
|
|
const auto& library = Library::Handle(Z, super_klass.library());
|
|
return library.url() == Symbols::DartFfi().ptr();
|
|
}
|
|
|
|
static Dart_WeakPersistentHandle AllocateWeakPersistentHandle(
|
|
Thread* thread,
|
|
const Object& ref,
|
|
void* peer,
|
|
intptr_t external_allocation_size,
|
|
Dart_HandleFinalizer callback) {
|
|
if (!ref.ptr()->IsHeapObject()) {
|
|
return NULL;
|
|
}
|
|
if (ref.IsPointer()) {
|
|
return NULL;
|
|
}
|
|
if (IsFfiCompound(thread, ref)) {
|
|
return NULL;
|
|
}
|
|
|
|
FinalizablePersistentHandle* finalizable_ref =
|
|
FinalizablePersistentHandle::New(thread->isolate_group(), ref, peer,
|
|
callback, external_allocation_size,
|
|
/*auto_delete=*/false);
|
|
return finalizable_ref->ApiWeakPersistentHandle();
|
|
}
|
|
|
|
static Dart_WeakPersistentHandle AllocateWeakPersistentHandle(
|
|
Thread* T,
|
|
Dart_Handle object,
|
|
void* peer,
|
|
intptr_t external_allocation_size,
|
|
Dart_HandleFinalizer callback) {
|
|
const auto& ref = Object::Handle(Z, Api::UnwrapHandle(object));
|
|
return AllocateWeakPersistentHandle(T, ref, peer, external_allocation_size,
|
|
callback);
|
|
}
|
|
|
|
static Dart_FinalizableHandle AllocateFinalizableHandle(
|
|
Thread* thread,
|
|
const Object& ref,
|
|
void* peer,
|
|
intptr_t external_allocation_size,
|
|
Dart_HandleFinalizer callback) {
|
|
if (!ref.ptr()->IsHeapObject()) {
|
|
return NULL;
|
|
}
|
|
if (ref.IsPointer()) {
|
|
return NULL;
|
|
}
|
|
if (IsFfiCompound(thread, ref)) {
|
|
return NULL;
|
|
}
|
|
|
|
FinalizablePersistentHandle* finalizable_ref =
|
|
FinalizablePersistentHandle::New(thread->isolate_group(), ref, peer,
|
|
callback, external_allocation_size,
|
|
/*auto_delete=*/true);
|
|
return finalizable_ref->ApiFinalizableHandle();
|
|
}
|
|
|
|
static Dart_FinalizableHandle AllocateFinalizableHandle(
|
|
Thread* T,
|
|
Dart_Handle object,
|
|
void* peer,
|
|
intptr_t external_allocation_size,
|
|
Dart_HandleFinalizer callback) {
|
|
const auto& ref = Object::Handle(Z, Api::UnwrapHandle(object));
|
|
return AllocateFinalizableHandle(T, ref, peer, external_allocation_size,
|
|
callback);
|
|
}
|
|
|
|
DART_EXPORT Dart_WeakPersistentHandle
|
|
Dart_NewWeakPersistentHandle(Dart_Handle object,
|
|
void* peer,
|
|
intptr_t external_allocation_size,
|
|
Dart_HandleFinalizer callback) {
|
|
DARTSCOPE(Thread::Current());
|
|
if (callback == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return AllocateWeakPersistentHandle(T, object, peer, external_allocation_size,
|
|
callback);
|
|
}
|
|
|
|
DART_EXPORT Dart_FinalizableHandle
|
|
Dart_NewFinalizableHandle(Dart_Handle object,
|
|
void* peer,
|
|
intptr_t external_allocation_size,
|
|
Dart_HandleFinalizer callback) {
|
|
DARTSCOPE(Thread::Current());
|
|
if (callback == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
return AllocateFinalizableHandle(T, object, peer, external_allocation_size,
|
|
callback);
|
|
}
|
|
|
|
DART_EXPORT void Dart_UpdateExternalSize(Dart_WeakPersistentHandle object,
|
|
intptr_t external_size) {
|
|
Thread* T = Thread::Current();
|
|
IsolateGroup* isolate_group = T->isolate_group();
|
|
CHECK_ISOLATE_GROUP(isolate_group);
|
|
TransitionToVM transition(T);
|
|
ApiState* state = isolate_group->api_state();
|
|
ASSERT(state != NULL);
|
|
ASSERT(state->IsActiveWeakPersistentHandle(object));
|
|
auto weak_ref = FinalizablePersistentHandle::Cast(object);
|
|
weak_ref->UpdateExternalSize(external_size, isolate_group);
|
|
}
|
|
|
|
DART_EXPORT void Dart_UpdateFinalizableExternalSize(
|
|
Dart_FinalizableHandle object,
|
|
Dart_Handle strong_ref_to_object,
|
|
intptr_t external_allocation_size) {
|
|
if (!::Dart_IdentityEquals(strong_ref_to_object,
|
|
HandleFromFinalizable(object))) {
|
|
FATAL1(
|
|
"%s expects arguments 'object' and 'strong_ref_to_object' to point to "
|
|
"the same object.",
|
|
CURRENT_FUNC);
|
|
}
|
|
auto wph_object = reinterpret_cast<Dart_WeakPersistentHandle>(object);
|
|
::Dart_UpdateExternalSize(wph_object, external_allocation_size);
|
|
}
|
|
|
|
DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object) {
|
|
Thread* T = Thread::Current();
|
|
IsolateGroup* isolate_group = T->isolate_group();
|
|
CHECK_ISOLATE_GROUP(isolate_group);
|
|
TransitionToVM transition(T);
|
|
ApiState* state = isolate_group->api_state();
|
|
ASSERT(state != NULL);
|
|
ASSERT(state->IsActivePersistentHandle(object));
|
|
ASSERT(!Api::IsProtectedHandle(object));
|
|
if (!Api::IsProtectedHandle(object)) {
|
|
PersistentHandle* ref = PersistentHandle::Cast(object);
|
|
state->FreePersistentHandle(ref);
|
|
}
|
|
}
|
|
|
|
DART_EXPORT void Dart_DeleteWeakPersistentHandle(
|
|
Dart_WeakPersistentHandle object) {
|
|
Thread* T = Thread::Current();
|
|
IsolateGroup* isolate_group = T->isolate_group();
|
|
CHECK_ISOLATE_GROUP(isolate_group);
|
|
TransitionToVM transition(T);
|
|
ApiState* state = isolate_group->api_state();
|
|
ASSERT(state != NULL);
|
|
ASSERT(state->IsActiveWeakPersistentHandle(object));
|
|
auto weak_ref = FinalizablePersistentHandle::Cast(object);
|
|
weak_ref->EnsureFreedExternal(isolate_group);
|
|
state->FreeWeakPersistentHandle(weak_ref);
|
|
}
|
|
|
|
DART_EXPORT void Dart_DeleteFinalizableHandle(
|
|
Dart_FinalizableHandle object,
|
|
Dart_Handle strong_ref_to_object) {
|
|
if (!::Dart_IdentityEquals(strong_ref_to_object,
|
|
HandleFromFinalizable(object))) {
|
|
FATAL1(
|
|
"%s expects arguments 'object' and 'strong_ref_to_object' to point to "
|
|
"the same object.",
|
|
CURRENT_FUNC);
|
|
}
|
|
|
|
auto wph_object = reinterpret_cast<Dart_WeakPersistentHandle>(object);
|
|
|
|
::Dart_DeleteWeakPersistentHandle(wph_object);
|
|
}
|
|
|
|
// --- Initialization and Globals ---
|
|
|
|
DART_EXPORT const char* Dart_VersionString() {
|
|
return Version::String();
|
|
}
|
|
|
|
DART_EXPORT char* Dart_Initialize(Dart_InitializeParams* params) {
|
|
if (params == NULL) {
|
|
return Utils::StrDup(
|
|
"Dart_Initialize: "
|
|
"Dart_InitializeParams is null.");
|
|
}
|
|
|
|
if (params->version != DART_INITIALIZE_PARAMS_CURRENT_VERSION) {
|
|
return Utils::StrDup(
|
|
"Dart_Initialize: "
|
|
"Invalid Dart_InitializeParams version.");
|
|
}
|
|
|
|
return Dart::Init(params);
|
|
}
|
|
|
|
DART_EXPORT char* Dart_Cleanup() {
|
|
CHECK_NO_ISOLATE(Isolate::Current());
|
|
return Dart::Cleanup();
|
|
}
|
|
|
|
DART_EXPORT char* Dart_SetVMFlags(int argc, const char** argv) {
|
|
return Flags::ProcessCommandLineFlags(argc, argv);
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsVMFlagSet(const char* flag_name) {
|
|
return Flags::IsSet(flag_name);
|
|
}
|
|
|
|
#if !defined(PRODUCT)
|
|
#define VM_METRIC_API(type, variable, name, unit) \
|
|
DART_EXPORT int64_t Dart_VM##variable##Metric() { \
|
|
return vm_metric_##variable.Value(); \
|
|
}
|
|
VM_METRIC_LIST(VM_METRIC_API);
|
|
#undef VM_METRIC_API
|
|
#else // !defined(PRODUCT)
|
|
#define VM_METRIC_API(type, variable, name, unit) \
|
|
DART_EXPORT int64_t Dart_VM##variable##Metric() { return -1; }
|
|
VM_METRIC_LIST(VM_METRIC_API)
|
|
#undef VM_METRIC_API
|
|
#endif // !defined(PRODUCT)
|
|
|
|
#define ISOLATE_GROUP_METRIC_API(type, variable, name, unit) \
|
|
DART_EXPORT int64_t Dart_IsolateGroup##variable##Metric( \
|
|
Dart_IsolateGroup isolate_group) { \
|
|
if (isolate_group == nullptr) { \
|
|
FATAL1("%s expects argument 'isolate_group' to be non-null.", \
|
|
CURRENT_FUNC); \
|
|
} \
|
|
IsolateGroup* group = reinterpret_cast<IsolateGroup*>(isolate_group); \
|
|
return group->Get##variable##Metric()->Value(); \
|
|
}
|
|
ISOLATE_GROUP_METRIC_LIST(ISOLATE_GROUP_METRIC_API)
|
|
#undef ISOLATE_GROUP_METRIC_API
|
|
|
|
#if !defined(PRODUCT)
|
|
#define ISOLATE_METRIC_API(type, variable, name, unit) \
|
|
DART_EXPORT int64_t Dart_Isolate##variable##Metric(Dart_Isolate isolate) { \
|
|
if (isolate == NULL) { \
|
|
FATAL1("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC); \
|
|
} \
|
|
Isolate* iso = reinterpret_cast<Isolate*>(isolate); \
|
|
return iso->Get##variable##Metric()->Value(); \
|
|
}
|
|
ISOLATE_METRIC_LIST(ISOLATE_METRIC_API)
|
|
#undef ISOLATE_METRIC_API
|
|
#else // !defined(PRODUCT)
|
|
#define ISOLATE_METRIC_API(type, variable, name, unit) \
|
|
DART_EXPORT int64_t Dart_Isolate##variable##Metric(Dart_Isolate isolate) { \
|
|
return -1; \
|
|
}
|
|
ISOLATE_METRIC_LIST(ISOLATE_METRIC_API)
|
|
#undef ISOLATE_METRIC_API
|
|
#endif // !defined(PRODUCT)
|
|
|
|
// --- Isolates ---
|
|
|
|
static Dart_Isolate CreateIsolate(IsolateGroup* group,
|
|
bool is_new_group,
|
|
const char* name,
|
|
void* isolate_data,
|
|
char** error) {
|
|
CHECK_NO_ISOLATE(Isolate::Current());
|
|
|
|
auto source = group->source();
|
|
Isolate* I = Dart::CreateIsolate(name, source->flags, group);
|
|
if (I == NULL) {
|
|
if (error != NULL) {
|
|
*error = Utils::StrDup("Isolate creation failed");
|
|
}
|
|
return reinterpret_cast<Dart_Isolate>(NULL);
|
|
}
|
|
|
|
Thread* T = Thread::Current();
|
|
bool success = false;
|
|
{
|
|
StackZone zone(T);
|
|
// We enter an API scope here as InitializeIsolate could compile some
|
|
// bootstrap library files which call out to a tag handler that may create
|
|
// Api Handles when an error is encountered.
|
|
T->EnterApiScope();
|
|
const Error& error_obj = Error::Handle(
|
|
Z, Dart::InitializeIsolate(
|
|
source->snapshot_data, source->snapshot_instructions,
|
|
source->kernel_buffer, source->kernel_buffer_size,
|
|
is_new_group ? nullptr : group, isolate_data));
|
|
if (error_obj.IsNull()) {
|
|
#if defined(DEBUG) && !defined(DART_PRECOMPILED_RUNTIME)
|
|
if (FLAG_check_function_fingerprints && !FLAG_precompiled_mode) {
|
|
Library::CheckFunctionFingerprints();
|
|
}
|
|
#endif // defined(DEBUG) && !defined(DART_PRECOMPILED_RUNTIME).
|
|
success = true;
|
|
} else if (error != NULL) {
|
|
*error = Utils::StrDup(error_obj.ToErrorCString());
|
|
}
|
|
// We exit the API scope entered above.
|
|
T->ExitApiScope();
|
|
}
|
|
|
|
if (success) {
|
|
// A Thread structure has been associated to the thread, we do the
|
|
// safepoint transition explicitly here instead of using the
|
|
// TransitionXXX scope objects as the reverse transition happens
|
|
// outside this scope in Dart_ShutdownIsolate/Dart_ExitIsolate.
|
|
T->set_execution_state(Thread::kThreadInNative);
|
|
T->EnterSafepoint();
|
|
if (error != NULL) {
|
|
*error = NULL;
|
|
}
|
|
return Api::CastIsolate(I);
|
|
}
|
|
|
|
Dart::ShutdownIsolate();
|
|
return reinterpret_cast<Dart_Isolate>(NULL);
|
|
}
|
|
|
|
static bool IsServiceOrKernelIsolateName(const char* name) {
|
|
if (ServiceIsolate::NameEquals(name)) {
|
|
ASSERT(!ServiceIsolate::Exists());
|
|
return true;
|
|
}
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
if (KernelIsolate::NameEquals(name)) {
|
|
ASSERT(!KernelIsolate::Exists());
|
|
return true;
|
|
}
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
return false;
|
|
}
|
|
|
|
Isolate* CreateWithinExistingIsolateGroup(IsolateGroup* group,
|
|
const char* name,
|
|
char** error) {
|
|
API_TIMELINE_DURATION(Thread::Current());
|
|
CHECK_NO_ISOLATE(Isolate::Current());
|
|
|
|
auto spawning_group = group;
|
|
|
|
Isolate* isolate = reinterpret_cast<Isolate*>(
|
|
CreateIsolate(spawning_group, /*is_new_group=*/false, name,
|
|
/*isolate_data=*/nullptr, error));
|
|
if (isolate == nullptr) return nullptr;
|
|
|
|
auto source = spawning_group->source();
|
|
ASSERT(isolate->source() == source);
|
|
|
|
return isolate;
|
|
}
|
|
|
|
DART_EXPORT void Dart_IsolateFlagsInitialize(Dart_IsolateFlags* flags) {
|
|
Isolate::FlagsInitialize(flags);
|
|
}
|
|
|
|
DART_EXPORT Dart_Isolate
|
|
Dart_CreateIsolateGroup(const char* script_uri,
|
|
const char* name,
|
|
const uint8_t* snapshot_data,
|
|
const uint8_t* snapshot_instructions,
|
|
Dart_IsolateFlags* flags,
|
|
void* isolate_group_data,
|
|
void* isolate_data,
|
|
char** error) {
|
|
API_TIMELINE_DURATION(Thread::Current());
|
|
|
|
Dart_IsolateFlags api_flags;
|
|
if (flags == nullptr) {
|
|
Isolate::FlagsInitialize(&api_flags);
|
|
flags = &api_flags;
|
|
}
|
|
|
|
const char* non_null_name = name == nullptr ? "isolate" : name;
|
|
std::unique_ptr<IsolateGroupSource> source(
|
|
new IsolateGroupSource(script_uri, non_null_name, snapshot_data,
|
|
snapshot_instructions, nullptr, -1, *flags));
|
|
auto group = new IsolateGroup(std::move(source), isolate_group_data, *flags);
|
|
group->CreateHeap(
|
|
/*is_vm_isolate=*/false, IsServiceOrKernelIsolateName(non_null_name));
|
|
IsolateGroup::RegisterIsolateGroup(group);
|
|
Dart_Isolate isolate = CreateIsolate(group, /*is_new_group=*/true,
|
|
non_null_name, isolate_data, error);
|
|
if (isolate != nullptr) {
|
|
group->set_initial_spawn_successful();
|
|
}
|
|
return isolate;
|
|
}
|
|
|
|
DART_EXPORT Dart_Isolate
|
|
Dart_CreateIsolateGroupFromKernel(const char* script_uri,
|
|
const char* name,
|
|
const uint8_t* kernel_buffer,
|
|
intptr_t kernel_buffer_size,
|
|
Dart_IsolateFlags* flags,
|
|
void* isolate_group_data,
|
|
void* isolate_data,
|
|
char** error) {
|
|
API_TIMELINE_DURATION(Thread::Current());
|
|
|
|
Dart_IsolateFlags api_flags;
|
|
if (flags == nullptr) {
|
|
Isolate::FlagsInitialize(&api_flags);
|
|
flags = &api_flags;
|
|
}
|
|
|
|
const char* non_null_name = name == nullptr ? "isolate" : name;
|
|
std::shared_ptr<IsolateGroupSource> source(
|
|
new IsolateGroupSource(script_uri, non_null_name, nullptr, nullptr,
|
|
kernel_buffer, kernel_buffer_size, *flags));
|
|
auto group = new IsolateGroup(source, isolate_group_data, *flags);
|
|
IsolateGroup::RegisterIsolateGroup(group);
|
|
group->CreateHeap(
|
|
/*is_vm_isolate=*/false, IsServiceOrKernelIsolateName(non_null_name));
|
|
Dart_Isolate isolate = CreateIsolate(group, /*is_new_group=*/true,
|
|
non_null_name, isolate_data, error);
|
|
if (isolate != nullptr) {
|
|
group->set_initial_spawn_successful();
|
|
}
|
|
return isolate;
|
|
}
|
|
|
|
DART_EXPORT Dart_Isolate
|
|
Dart_CreateIsolateInGroup(Dart_Isolate group_member,
|
|
const char* name,
|
|
Dart_IsolateShutdownCallback shutdown_callback,
|
|
Dart_IsolateCleanupCallback cleanup_callback,
|
|
void* child_isolate_data,
|
|
char** error) {
|
|
CHECK_NO_ISOLATE(Isolate::Current());
|
|
auto member = reinterpret_cast<Isolate*>(group_member);
|
|
if (member->IsScheduled()) {
|
|
FATAL("The given member isolate (%s) must not have been entered.",
|
|
member->name());
|
|
}
|
|
|
|
*error = nullptr;
|
|
|
|
Isolate* isolate;
|
|
isolate = CreateWithinExistingIsolateGroup(member->group(), name, error);
|
|
if (isolate != nullptr) {
|
|
isolate->set_origin_id(member->origin_id());
|
|
isolate->set_init_callback_data(child_isolate_data);
|
|
isolate->set_on_shutdown_callback(shutdown_callback);
|
|
isolate->set_on_cleanup_callback(cleanup_callback);
|
|
}
|
|
|
|
return Api::CastIsolate(isolate);
|
|
}
|
|
|
|
DART_EXPORT void Dart_ShutdownIsolate() {
|
|
Thread* T = Thread::Current();
|
|
auto I = T->isolate();
|
|
CHECK_ISOLATE(I);
|
|
|
|
// The Thread structure is disassociated from the isolate, we do the
|
|
// safepoint transition explicitly here instead of using the TransitionXXX
|
|
// scope objects as the original transition happened outside this scope in
|
|
// Dart_EnterIsolate/Dart_CreateIsolateGroup.
|
|
ASSERT(T->execution_state() == Thread::kThreadInNative);
|
|
T->ExitSafepoint();
|
|
T->set_execution_state(Thread::kThreadInVM);
|
|
|
|
I->WaitForOutstandingSpawns();
|
|
|
|
// Release any remaining API scopes.
|
|
ApiLocalScope* scope = T->api_top_scope();
|
|
while (scope != NULL) {
|
|
ApiLocalScope* previous = scope->previous();
|
|
delete scope;
|
|
scope = previous;
|
|
}
|
|
T->set_api_top_scope(NULL);
|
|
|
|
{
|
|
StackZone zone(T);
|
|
HandleScope handle_scope(T);
|
|
#if defined(DEBUG)
|
|
if (T->isolate()->origin_id() == 0) {
|
|
T->isolate_group()->ValidateConstants();
|
|
}
|
|
#endif
|
|
Dart::RunShutdownCallback();
|
|
}
|
|
Dart::ShutdownIsolate();
|
|
}
|
|
|
|
DART_EXPORT Dart_Isolate Dart_CurrentIsolate() {
|
|
return Api::CastIsolate(Isolate::Current());
|
|
}
|
|
|
|
DART_EXPORT void* Dart_CurrentIsolateData() {
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
NoSafepointScope no_safepoint_scope;
|
|
return isolate->init_callback_data();
|
|
}
|
|
|
|
DART_EXPORT void* Dart_IsolateData(Dart_Isolate isolate) {
|
|
if (isolate == NULL) {
|
|
FATAL1("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC);
|
|
}
|
|
// TODO(http://dartbug.com/16615): Validate isolate parameter.
|
|
return reinterpret_cast<Isolate*>(isolate)->init_callback_data();
|
|
}
|
|
|
|
DART_EXPORT Dart_IsolateGroup Dart_CurrentIsolateGroup() {
|
|
return Api::CastIsolateGroup(IsolateGroup::Current());
|
|
}
|
|
|
|
DART_EXPORT void* Dart_CurrentIsolateGroupData() {
|
|
IsolateGroup* isolate_group = IsolateGroup::Current();
|
|
CHECK_ISOLATE_GROUP(isolate_group);
|
|
NoSafepointScope no_safepoint_scope;
|
|
return isolate_group->embedder_data();
|
|
}
|
|
|
|
DART_EXPORT Dart_IsolateGroupId Dart_CurrentIsolateGroupId() {
|
|
IsolateGroup* isolate_group = IsolateGroup::Current();
|
|
CHECK_ISOLATE_GROUP(isolate_group);
|
|
return isolate_group->id();
|
|
}
|
|
|
|
DART_EXPORT void* Dart_IsolateGroupData(Dart_Isolate isolate) {
|
|
if (isolate == NULL) {
|
|
FATAL1("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC);
|
|
}
|
|
// TODO(http://dartbug.com/16615): Validate isolate parameter.
|
|
return reinterpret_cast<Isolate*>(isolate)->group()->embedder_data();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_DebugName() {
|
|
DARTSCOPE(Thread::Current());
|
|
Isolate* I = T->isolate();
|
|
return Api::NewHandle(
|
|
T, String::NewFormatted("(%" Pd64 ") '%s'",
|
|
static_cast<int64_t>(I->main_port()), I->name()));
|
|
}
|
|
|
|
DART_EXPORT const char* Dart_IsolateServiceId(Dart_Isolate isolate) {
|
|
if (isolate == NULL) {
|
|
FATAL1("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC);
|
|
}
|
|
// TODO(http://dartbug.com/16615): Validate isolate parameter.
|
|
Isolate* I = reinterpret_cast<Isolate*>(isolate);
|
|
int64_t main_port = static_cast<int64_t>(I->main_port());
|
|
return OS::SCreate(NULL, "isolates/%" Pd64, main_port);
|
|
}
|
|
|
|
DART_EXPORT void Dart_EnterIsolate(Dart_Isolate isolate) {
|
|
CHECK_NO_ISOLATE(Isolate::Current());
|
|
// TODO(http://dartbug.com/16615): Validate isolate parameter.
|
|
Isolate* iso = reinterpret_cast<Isolate*>(isolate);
|
|
if (!Thread::EnterIsolate(iso)) {
|
|
if (iso->IsScheduled()) {
|
|
FATAL(
|
|
"Isolate %s is already scheduled on mutator thread %p, "
|
|
"failed to schedule from os thread 0x%" Px "\n",
|
|
iso->name(), iso->scheduled_mutator_thread(),
|
|
OSThread::ThreadIdToIntPtr(OSThread::GetCurrentThreadId()));
|
|
} else {
|
|
FATAL("Unable to enter isolate %s as Dart VM is shutting down",
|
|
iso->name());
|
|
}
|
|
}
|
|
// A Thread structure has been associated to the thread, we do the
|
|
// safepoint transition explicitly here instead of using the
|
|
// TransitionXXX scope objects as the reverse transition happens
|
|
// outside this scope in Dart_ExitIsolate/Dart_ShutdownIsolate.
|
|
Thread* T = Thread::Current();
|
|
T->set_execution_state(Thread::kThreadInNative);
|
|
T->EnterSafepoint();
|
|
}
|
|
|
|
DART_EXPORT void Dart_StartProfiling() {
|
|
#if !defined(PRODUCT)
|
|
if (!FLAG_profiler) {
|
|
FLAG_profiler = true;
|
|
Profiler::Init();
|
|
}
|
|
#endif // !defined(PRODUCT)
|
|
}
|
|
|
|
DART_EXPORT void Dart_StopProfiling() {
|
|
#if !defined(PRODUCT)
|
|
if (FLAG_profiler) {
|
|
Profiler::Cleanup();
|
|
FLAG_profiler = false;
|
|
}
|
|
#endif // !defined(PRODUCT)
|
|
}
|
|
|
|
DART_EXPORT void Dart_ThreadDisableProfiling() {
|
|
OSThread* os_thread = OSThread::Current();
|
|
if (os_thread == NULL) {
|
|
return;
|
|
}
|
|
os_thread->DisableThreadInterrupts();
|
|
}
|
|
|
|
DART_EXPORT void Dart_ThreadEnableProfiling() {
|
|
OSThread* os_thread = OSThread::Current();
|
|
if (os_thread == NULL) {
|
|
return;
|
|
}
|
|
os_thread->EnableThreadInterrupts();
|
|
}
|
|
|
|
DART_EXPORT void Dart_AddSymbols(const char* dso_name,
|
|
void* buffer,
|
|
intptr_t buffer_size) {
|
|
NativeSymbolResolver::AddSymbols(dso_name, buffer, buffer_size);
|
|
}
|
|
|
|
DART_EXPORT bool Dart_WriteProfileToTimeline(Dart_Port main_port,
|
|
char** error) {
|
|
#if defined(PRODUCT)
|
|
return false;
|
|
#else
|
|
if (!FLAG_profiler) {
|
|
if (error != NULL) {
|
|
*error = Utils::StrDup("The profiler is not running.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const intptr_t kBufferLength = 512;
|
|
char method[kBufferLength];
|
|
|
|
// clang-format off
|
|
intptr_t method_length = snprintf(method, kBufferLength, "{"
|
|
"\"jsonrpc\": \"2.0\","
|
|
"\"method\": \"_writeCpuProfileTimeline\","
|
|
"\"id\": \"\","
|
|
"\"params\": {"
|
|
" \"isolateId\": \"isolates/%" Pd64 "\","
|
|
" \"tags\": \"None\""
|
|
"}"
|
|
"}", main_port);
|
|
// clang-format on
|
|
ASSERT(method_length <= kBufferLength);
|
|
|
|
char* response = NULL;
|
|
intptr_t response_length;
|
|
bool success = Dart_InvokeVMServiceMethod(
|
|
reinterpret_cast<uint8_t*>(method), method_length,
|
|
reinterpret_cast<uint8_t**>(&response), &response_length, error);
|
|
free(response);
|
|
return success;
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT bool Dart_ShouldPauseOnStart() {
|
|
#if defined(PRODUCT)
|
|
return false;
|
|
#else
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
NoSafepointScope no_safepoint_scope;
|
|
return isolate->message_handler()->should_pause_on_start();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT void Dart_SetShouldPauseOnStart(bool should_pause) {
|
|
#if defined(PRODUCT)
|
|
if (should_pause) {
|
|
FATAL1("%s(true) is not supported in a PRODUCT build", CURRENT_FUNC);
|
|
}
|
|
#else
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
NoSafepointScope no_safepoint_scope;
|
|
if (isolate->is_runnable()) {
|
|
FATAL1("%s expects the current isolate to not be runnable yet.",
|
|
CURRENT_FUNC);
|
|
}
|
|
isolate->message_handler()->set_should_pause_on_start(should_pause);
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsPausedOnStart() {
|
|
#if defined(PRODUCT)
|
|
return false;
|
|
#else
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
NoSafepointScope no_safepoint_scope;
|
|
return isolate->message_handler()->is_paused_on_start();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT void Dart_SetPausedOnStart(bool paused) {
|
|
#if defined(PRODUCT)
|
|
if (paused) {
|
|
FATAL1("%s(true) is not supported in a PRODUCT build", CURRENT_FUNC);
|
|
}
|
|
#else
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
NoSafepointScope no_safepoint_scope;
|
|
if (isolate->message_handler()->is_paused_on_start() != paused) {
|
|
isolate->message_handler()->PausedOnStart(paused);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT bool Dart_ShouldPauseOnExit() {
|
|
#if defined(PRODUCT)
|
|
return false;
|
|
#else
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
NoSafepointScope no_safepoint_scope;
|
|
return isolate->message_handler()->should_pause_on_exit();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT void Dart_SetShouldPauseOnExit(bool should_pause) {
|
|
#if defined(PRODUCT)
|
|
if (should_pause) {
|
|
FATAL1("%s(true) is not supported in a PRODUCT build", CURRENT_FUNC);
|
|
}
|
|
#else
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
NoSafepointScope no_safepoint_scope;
|
|
isolate->message_handler()->set_should_pause_on_exit(should_pause);
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsPausedOnExit() {
|
|
#if defined(PRODUCT)
|
|
return false;
|
|
#else
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
NoSafepointScope no_safepoint_scope;
|
|
return isolate->message_handler()->is_paused_on_exit();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT void Dart_SetPausedOnExit(bool paused) {
|
|
#if defined(PRODUCT)
|
|
if (paused) {
|
|
FATAL1("%s(true) is not supported in a PRODUCT build", CURRENT_FUNC);
|
|
}
|
|
#else
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
NoSafepointScope no_safepoint_scope;
|
|
if (isolate->message_handler()->is_paused_on_exit() != paused) {
|
|
isolate->message_handler()->PausedOnExit(paused);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT void Dart_SetStickyError(Dart_Handle error) {
|
|
Thread* thread = Thread::Current();
|
|
DARTSCOPE(thread);
|
|
Isolate* isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
NoSafepointScope no_safepoint_scope;
|
|
const Error& error_handle = Api::UnwrapErrorHandle(Z, error);
|
|
if ((isolate->sticky_error() != Error::null()) &&
|
|
(error_handle.ptr() != Object::null())) {
|
|
FATAL1("%s expects there to be no sticky error.", CURRENT_FUNC);
|
|
}
|
|
if (!error_handle.IsUnhandledException() &&
|
|
(error_handle.ptr() != Object::null())) {
|
|
FATAL1("%s expects the error to be an unhandled exception error or null.",
|
|
CURRENT_FUNC);
|
|
}
|
|
isolate->SetStickyError(error_handle.ptr());
|
|
}
|
|
|
|
DART_EXPORT bool Dart_HasStickyError() {
|
|
Thread* T = Thread::Current();
|
|
Isolate* isolate = T->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
NoSafepointScope no_safepoint_scope;
|
|
return isolate->sticky_error() != Error::null();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetStickyError() {
|
|
Thread* T = Thread::Current();
|
|
Isolate* I = T->isolate();
|
|
CHECK_ISOLATE(I);
|
|
{
|
|
NoSafepointScope no_safepoint_scope;
|
|
if (I->sticky_error() == Error::null()) {
|
|
return Api::Null();
|
|
}
|
|
}
|
|
TransitionNativeToVM transition(T);
|
|
return Api::NewHandle(T, I->sticky_error());
|
|
}
|
|
|
|
DART_EXPORT void Dart_NotifyIdle(int64_t deadline) {
|
|
Thread* T = Thread::Current();
|
|
CHECK_ISOLATE(T->isolate());
|
|
API_TIMELINE_BEGIN_END(T);
|
|
TransitionNativeToVM transition(T);
|
|
T->isolate()->group()->idle_time_handler()->NotifyIdle(deadline);
|
|
}
|
|
|
|
DART_EXPORT void Dart_NotifyLowMemory() {
|
|
API_TIMELINE_BEGIN_END(Thread::Current());
|
|
SemiSpace::ClearCache();
|
|
Zone::ClearCache();
|
|
|
|
// For each isolate's global variables, we might also clear:
|
|
// - RegExp backtracking stack (both bytecode and compiled versions)
|
|
// - String -> RegExp cache
|
|
// - BigInt division/remainder cache
|
|
// - double.toString cache
|
|
// But cache invalidation code might be larger than the expected size of some
|
|
// caches.
|
|
}
|
|
|
|
DART_EXPORT Dart_PerformanceMode
|
|
Dart_SetPerformanceMode(Dart_PerformanceMode mode) {
|
|
Thread* T = Thread::Current();
|
|
CHECK_ISOLATE(T->isolate());
|
|
TransitionNativeToVM transition(T);
|
|
return T->heap()->SetMode(mode);
|
|
}
|
|
|
|
DART_EXPORT void Dart_ExitIsolate() {
|
|
Thread* T = Thread::Current();
|
|
CHECK_ISOLATE(T->isolate());
|
|
// The Thread structure is disassociated from the isolate, we do the
|
|
// safepoint transition explicitly here instead of using the TransitionXXX
|
|
// scope objects as the original transition happened outside this scope in
|
|
// Dart_EnterIsolate/Dart_CreateIsolateGroup.
|
|
ASSERT(T->execution_state() == Thread::kThreadInNative);
|
|
T->ExitSafepoint();
|
|
T->set_execution_state(Thread::kThreadInVM);
|
|
Thread::ExitIsolate();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_CreateSnapshot(uint8_t** vm_snapshot_data_buffer,
|
|
intptr_t* vm_snapshot_data_size,
|
|
uint8_t** isolate_snapshot_data_buffer,
|
|
intptr_t* isolate_snapshot_data_size,
|
|
bool is_core) {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
return Api::NewError("Cannot create snapshots on an AOT runtime.");
|
|
#else
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
if (vm_snapshot_data_buffer != nullptr) {
|
|
CHECK_NULL(vm_snapshot_data_size);
|
|
}
|
|
CHECK_NULL(isolate_snapshot_data_buffer);
|
|
CHECK_NULL(isolate_snapshot_data_size);
|
|
// Finalize all classes if needed.
|
|
Dart_Handle state = Api::CheckAndFinalizePendingClasses(T);
|
|
if (Api::IsError(state)) {
|
|
return state;
|
|
}
|
|
NoBackgroundCompilerScope no_bg_compiler(T);
|
|
|
|
#if defined(DEBUG)
|
|
T->isolate_group()->heap()->CollectAllGarbage(GCReason::kDebugging);
|
|
{
|
|
HeapIterationScope iteration(T);
|
|
CheckFunctionTypesVisitor check_canonical(T);
|
|
iteration.IterateObjects(&check_canonical);
|
|
}
|
|
#endif // #if defined(DEBUG)
|
|
|
|
ZoneWriteStream vm_snapshot_data(Api::TopScope(T)->zone(),
|
|
FullSnapshotWriter::kInitialSize);
|
|
ZoneWriteStream isolate_snapshot_data(Api::TopScope(T)->zone(),
|
|
FullSnapshotWriter::kInitialSize);
|
|
const Snapshot::Kind snapshot_kind =
|
|
is_core ? Snapshot::kFullCore : Snapshot::kFull;
|
|
FullSnapshotWriter writer(
|
|
snapshot_kind, &vm_snapshot_data, &isolate_snapshot_data,
|
|
nullptr /* vm_image_writer */, nullptr /* isolate_image_writer */);
|
|
writer.WriteFullSnapshot();
|
|
if (vm_snapshot_data_buffer != nullptr) {
|
|
*vm_snapshot_data_buffer = vm_snapshot_data.buffer();
|
|
*vm_snapshot_data_size = writer.VmIsolateSnapshotSize();
|
|
}
|
|
*isolate_snapshot_data_buffer = isolate_snapshot_data.buffer();
|
|
*isolate_snapshot_data_size = writer.IsolateSnapshotSize();
|
|
return Api::Success();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsKernel(const uint8_t* buffer, intptr_t buffer_size) {
|
|
if (buffer_size < 4) {
|
|
return false;
|
|
}
|
|
return (buffer[0] == 0x90) && (buffer[1] == 0xab) && (buffer[2] == 0xcd) &&
|
|
(buffer[3] == 0xef);
|
|
}
|
|
|
|
DART_EXPORT char* Dart_IsolateMakeRunnable(Dart_Isolate isolate) {
|
|
CHECK_NO_ISOLATE(Isolate::Current());
|
|
API_TIMELINE_DURATION(Thread::Current());
|
|
if (isolate == NULL) {
|
|
FATAL1("%s expects argument 'isolate' to be non-null.", CURRENT_FUNC);
|
|
}
|
|
// TODO(16615): Validate isolate parameter.
|
|
const char* error = reinterpret_cast<Isolate*>(isolate)->MakeRunnable();
|
|
if (error != nullptr) {
|
|
return Utils::StrDup(error);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// --- Messages and Ports ---
|
|
|
|
DART_EXPORT void Dart_SetMessageNotifyCallback(
|
|
Dart_MessageNotifyCallback message_notify_callback) {
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
|
|
{
|
|
NoSafepointScope no_safepoint_scope;
|
|
isolate->set_message_notify_callback(message_notify_callback);
|
|
}
|
|
|
|
if (message_notify_callback != nullptr && isolate->HasPendingMessages()) {
|
|
::Dart_ExitIsolate();
|
|
|
|
// If a new handler gets installed and there are pending messages in the
|
|
// queue (e.g. OOB messages for doing vm service work) we need to notify
|
|
// the newly registered callback, otherwise the embedder might never get
|
|
// notified about the pending messages.
|
|
message_notify_callback(Api::CastIsolate(isolate));
|
|
|
|
::Dart_EnterIsolate(Api::CastIsolate(isolate));
|
|
}
|
|
}
|
|
|
|
DART_EXPORT Dart_MessageNotifyCallback Dart_GetMessageNotifyCallback() {
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
NoSafepointScope no_safepoint_scope;
|
|
return isolate->message_notify_callback();
|
|
}
|
|
|
|
struct RunLoopData {
|
|
Monitor* monitor;
|
|
bool done;
|
|
};
|
|
|
|
static void RunLoopDone(uword param) {
|
|
RunLoopData* data = reinterpret_cast<RunLoopData*>(param);
|
|
ASSERT(data->monitor != NULL);
|
|
MonitorLocker ml(data->monitor);
|
|
data->done = true;
|
|
ml.Notify();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_RunLoop() {
|
|
Isolate* I;
|
|
bool result;
|
|
{
|
|
Thread* T = Thread::Current();
|
|
I = T->isolate();
|
|
CHECK_API_SCOPE(T);
|
|
CHECK_CALLBACK_STATE(T);
|
|
}
|
|
API_TIMELINE_BEGIN_END(Thread::Current());
|
|
// The message handler run loop does not expect to have a current isolate
|
|
// so we exit the isolate here and enter it again after the runloop is done.
|
|
::Dart_ExitIsolate();
|
|
{
|
|
Monitor monitor;
|
|
MonitorLocker ml(&monitor);
|
|
RunLoopData data;
|
|
data.monitor = &monitor;
|
|
data.done = false;
|
|
result =
|
|
I->message_handler()->Run(I->group()->thread_pool(), NULL, RunLoopDone,
|
|
reinterpret_cast<uword>(&data));
|
|
if (result) {
|
|
while (!data.done) {
|
|
ml.Wait();
|
|
}
|
|
}
|
|
}
|
|
::Dart_EnterIsolate(Api::CastIsolate(I));
|
|
if (!result) {
|
|
Thread* T = Thread::Current();
|
|
TransitionNativeToVM transition(T);
|
|
return Api::NewError("Run method in isolate message handler failed");
|
|
} else if (I->sticky_error() != Object::null()) {
|
|
Thread* T = Thread::Current();
|
|
TransitionNativeToVM transition(T);
|
|
return Api::NewHandle(T, I->StealStickyError());
|
|
}
|
|
if (FLAG_print_class_table) {
|
|
HANDLESCOPE(Thread::Current());
|
|
I->group()->class_table()->Print();
|
|
}
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT bool Dart_RunLoopAsync(bool errors_are_fatal,
|
|
Dart_Port on_error_port,
|
|
Dart_Port on_exit_port,
|
|
char** error) {
|
|
auto thread = Thread::Current();
|
|
auto isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
*error = nullptr;
|
|
|
|
if (thread->api_top_scope() != nullptr) {
|
|
*error = Utils::StrDup("There must not be an active api scope.");
|
|
return false;
|
|
}
|
|
|
|
if (!isolate->is_runnable()) {
|
|
const char* error_msg = isolate->MakeRunnable();
|
|
if (error_msg != nullptr) {
|
|
*error = Utils::StrDup(error_msg);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
isolate->SetErrorsFatal(errors_are_fatal);
|
|
|
|
if (on_error_port != ILLEGAL_PORT || on_exit_port != ILLEGAL_PORT) {
|
|
auto thread = Thread::Current();
|
|
TransitionNativeToVM transition(thread);
|
|
StackZone zone(thread);
|
|
|
|
if (on_error_port != ILLEGAL_PORT) {
|
|
const auto& port =
|
|
SendPort::Handle(thread->zone(), SendPort::New(on_error_port));
|
|
isolate->AddErrorListener(port);
|
|
}
|
|
if (on_exit_port != ILLEGAL_PORT) {
|
|
const auto& port =
|
|
SendPort::Handle(thread->zone(), SendPort::New(on_exit_port));
|
|
isolate->AddExitListener(port, Instance::null_instance());
|
|
}
|
|
}
|
|
|
|
Dart_ExitIsolate();
|
|
isolate->Run();
|
|
return true;
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_HandleMessage() {
|
|
Thread* T = Thread::Current();
|
|
Isolate* I = T->isolate();
|
|
CHECK_API_SCOPE(T);
|
|
CHECK_CALLBACK_STATE(T);
|
|
API_TIMELINE_BEGIN_END(T);
|
|
TransitionNativeToVM transition(T);
|
|
if (I->message_handler()->HandleNextMessage() != MessageHandler::kOK) {
|
|
return Api::NewHandle(T, T->StealStickyError());
|
|
}
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_WaitForEvent(int64_t timeout_millis) {
|
|
Thread* T = Thread::Current();
|
|
Isolate* I = T->isolate();
|
|
CHECK_API_SCOPE(T);
|
|
CHECK_CALLBACK_STATE(T);
|
|
API_TIMELINE_BEGIN_END(T);
|
|
TransitionNativeToVM transition(T);
|
|
if (I->message_notify_callback() != NULL) {
|
|
return Api::NewError("waitForEventSync is not supported by this embedder");
|
|
}
|
|
Object& result =
|
|
Object::Handle(Z, DartLibraryCalls::EnsureScheduleImmediate());
|
|
if (result.IsError()) {
|
|
return Api::NewHandle(T, result.ptr());
|
|
}
|
|
|
|
// Drain the microtask queue. Propagate any errors to the entry frame.
|
|
result = DartLibraryCalls::DrainMicrotaskQueue();
|
|
if (result.IsError()) {
|
|
// Persist the error across unwiding scopes before propagating.
|
|
const Error* error;
|
|
{
|
|
NoSafepointScope no_safepoint;
|
|
ErrorPtr raw_error = Error::Cast(result).ptr();
|
|
T->UnwindScopes(T->top_exit_frame_info());
|
|
error = &Error::Handle(T->zone(), raw_error);
|
|
}
|
|
Exceptions::PropagateToEntry(*error);
|
|
UNREACHABLE();
|
|
return Api::NewError("Unreachable");
|
|
}
|
|
|
|
// Block to wait for messages and then handle them. Propagate any errors to
|
|
// the entry frame.
|
|
if (I->message_handler()->PauseAndHandleAllMessages(timeout_millis) !=
|
|
MessageHandler::kOK) {
|
|
// Persist the error across unwiding scopes before propagating.
|
|
const Error* error;
|
|
{
|
|
NoSafepointScope no_safepoint;
|
|
ErrorPtr raw_error = T->StealStickyError();
|
|
T->UnwindScopes(T->top_exit_frame_info());
|
|
error = &Error::Handle(T->zone(), raw_error);
|
|
}
|
|
Exceptions::PropagateToEntry(*error);
|
|
UNREACHABLE();
|
|
return Api::NewError("Unreachable");
|
|
}
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT bool Dart_HandleServiceMessages() {
|
|
#if defined(PRODUCT)
|
|
return true;
|
|
#else
|
|
Thread* T = Thread::Current();
|
|
Isolate* I = T->isolate();
|
|
CHECK_API_SCOPE(T);
|
|
CHECK_CALLBACK_STATE(T);
|
|
API_TIMELINE_DURATION(T);
|
|
TransitionNativeToVM transition(T);
|
|
ASSERT(I->GetAndClearResumeRequest() == false);
|
|
MessageHandler::MessageStatus status =
|
|
I->message_handler()->HandleOOBMessages();
|
|
bool resume = I->GetAndClearResumeRequest();
|
|
return (status != MessageHandler::kOK) || resume;
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT bool Dart_HasServiceMessages() {
|
|
#if defined(PRODUCT)
|
|
return false;
|
|
#else
|
|
Isolate* isolate = Isolate::Current();
|
|
ASSERT(isolate);
|
|
NoSafepointScope no_safepoint_scope;
|
|
return isolate->message_handler()->HasOOBMessages();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT bool Dart_HasLivePorts() {
|
|
Isolate* isolate = Isolate::Current();
|
|
ASSERT(isolate);
|
|
NoSafepointScope no_safepoint_scope;
|
|
return isolate->message_handler()->HasLivePorts();
|
|
}
|
|
|
|
DART_EXPORT bool Dart_Post(Dart_Port port_id, Dart_Handle handle) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
NoSafepointScope no_safepoint_scope;
|
|
if (port_id == ILLEGAL_PORT) {
|
|
return false;
|
|
}
|
|
|
|
const Object& object = Object::Handle(Z, Api::UnwrapHandle(handle));
|
|
return PortMap::PostMessage(WriteMessage(/* can_send_any_object */ false,
|
|
/* same_group */ false, object,
|
|
port_id, Message::kNormalPriority));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewSendPort(Dart_Port port_id) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
if (port_id == ILLEGAL_PORT) {
|
|
return Api::NewError("%s: illegal port_id %" Pd64 ".", CURRENT_FUNC,
|
|
port_id);
|
|
}
|
|
return Api::NewHandle(T, SendPort::New(port_id));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_SendPortGetId(Dart_Handle port,
|
|
Dart_Port* port_id) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
API_TIMELINE_DURATION(T);
|
|
const SendPort& send_port = Api::UnwrapSendPortHandle(Z, port);
|
|
if (send_port.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, port, SendPort);
|
|
}
|
|
if (port_id == NULL) {
|
|
RETURN_NULL_ERROR(port_id);
|
|
}
|
|
*port_id = send_port.Id();
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Port Dart_GetMainPortId() {
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
return isolate->main_port();
|
|
}
|
|
|
|
// --- Scopes ----
|
|
|
|
DART_EXPORT void Dart_EnterScope() {
|
|
Thread* thread = Thread::Current();
|
|
Isolate* isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
TransitionNativeToVM transition(thread);
|
|
thread->EnterApiScope();
|
|
}
|
|
|
|
DART_EXPORT void Dart_ExitScope() {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_API_SCOPE(thread);
|
|
TransitionNativeToVM transition(thread);
|
|
thread->ExitApiScope();
|
|
}
|
|
|
|
DART_EXPORT uint8_t* Dart_ScopeAllocate(intptr_t size) {
|
|
Zone* zone;
|
|
Thread* thread = Thread::Current();
|
|
if (thread != NULL) {
|
|
ApiLocalScope* scope = thread->api_top_scope();
|
|
zone = scope->zone();
|
|
} else {
|
|
ApiNativeScope* scope = ApiNativeScope::Current();
|
|
if (scope == NULL) return NULL;
|
|
zone = scope->zone();
|
|
}
|
|
return reinterpret_cast<uint8_t*>(zone->AllocUnsafe(size));
|
|
}
|
|
|
|
// --- Objects ----
|
|
|
|
DART_EXPORT Dart_Handle Dart_Null() {
|
|
ASSERT(Isolate::Current() != NULL);
|
|
return Api::Null();
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsNull(Dart_Handle object) {
|
|
TransitionNativeToVM transition(Thread::Current());
|
|
return Api::UnwrapHandle(object) == Object::null();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_EmptyString() {
|
|
ASSERT(Isolate::Current() != NULL);
|
|
return Api::EmptyString();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_TypeDynamic() {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
API_TIMELINE_DURATION(T);
|
|
return Api::NewHandle(T, Type::DynamicType());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_TypeVoid() {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
API_TIMELINE_DURATION(T);
|
|
return Api::NewHandle(T, Type::VoidType());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_TypeNever() {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
API_TIMELINE_DURATION(T);
|
|
return Api::NewHandle(T, Type::NeverType());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_ObjectEquals(Dart_Handle obj1,
|
|
Dart_Handle obj2,
|
|
bool* value) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
const Instance& expected =
|
|
Instance::CheckedHandle(Z, Api::UnwrapHandle(obj1));
|
|
const Instance& actual = Instance::CheckedHandle(Z, Api::UnwrapHandle(obj2));
|
|
const Object& result =
|
|
Object::Handle(Z, DartLibraryCalls::Equals(expected, actual));
|
|
if (result.IsBool()) {
|
|
*value = Bool::Cast(result).value();
|
|
return Api::Success();
|
|
} else if (result.IsError()) {
|
|
return Api::NewHandle(T, result.ptr());
|
|
} else {
|
|
return Api::NewError("Expected boolean result from ==");
|
|
}
|
|
}
|
|
|
|
// Assumes type is non-null.
|
|
static bool InstanceIsType(const Thread* thread,
|
|
const Instance& instance,
|
|
const Type& type) {
|
|
ASSERT(!type.IsNull());
|
|
CHECK_CALLBACK_STATE(thread);
|
|
return instance.IsInstanceOf(type, Object::null_type_arguments(),
|
|
Object::null_type_arguments());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_ObjectIsType(Dart_Handle object,
|
|
Dart_Handle type,
|
|
bool* value) {
|
|
DARTSCOPE(Thread::Current());
|
|
|
|
const Type& type_obj = Api::UnwrapTypeHandle(Z, type);
|
|
if (type_obj.IsNull()) {
|
|
*value = false;
|
|
RETURN_TYPE_ERROR(Z, type, Type);
|
|
}
|
|
if (!type_obj.IsFinalized()) {
|
|
return Api::NewError(
|
|
"%s expects argument 'type' to be a fully resolved type.",
|
|
CURRENT_FUNC);
|
|
}
|
|
if (object == Api::Null()) {
|
|
*value = false;
|
|
return Api::Success();
|
|
}
|
|
const Instance& instance = Api::UnwrapInstanceHandle(Z, object);
|
|
if (instance.IsNull()) {
|
|
*value = false;
|
|
RETURN_TYPE_ERROR(Z, object, Instance);
|
|
}
|
|
*value = InstanceIsType(T, instance, type_obj);
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsInstance(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
REUSABLE_OBJECT_HANDLESCOPE(thread);
|
|
Object& ref = thread->ObjectHandle();
|
|
ref = Api::UnwrapHandle(object);
|
|
return ref.IsInstance();
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsNumber(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
return IsNumberClassId(Api::ClassId(object));
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsInteger(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
return IsIntegerClassId(Api::ClassId(object));
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsDouble(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
return Api::ClassId(object) == kDoubleCid;
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsBoolean(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
return Api::ClassId(object) == kBoolCid;
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsString(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
return IsStringClassId(Api::ClassId(object));
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsStringLatin1(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
return IsOneByteStringClassId(Api::ClassId(object));
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsExternalString(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
return IsExternalStringClassId(Api::ClassId(object));
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsList(Dart_Handle object) {
|
|
DARTSCOPE(Thread::Current());
|
|
if (IsBuiltinListClassId(Api::ClassId(object))) {
|
|
return true;
|
|
}
|
|
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(object));
|
|
return GetListInstance(Z, obj) != Instance::null();
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsMap(Dart_Handle object) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(object));
|
|
return GetMapInstance(Z, obj) != Instance::null();
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsLibrary(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
return Api::ClassId(object) == kLibraryCid;
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsType(Dart_Handle handle) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
return IsTypeClassId(Api::ClassId(handle));
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsFunction(Dart_Handle handle) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
return Api::ClassId(handle) == kFunctionCid;
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsVariable(Dart_Handle handle) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
return Api::ClassId(handle) == kFieldCid;
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsTypeVariable(Dart_Handle handle) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
return Api::ClassId(handle) == kTypeParameterCid;
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsClosure(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
return Api::ClassId(object) == kClosureCid;
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsTearOff(Dart_Handle object) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(object));
|
|
if (obj.IsClosure()) {
|
|
const Closure& closure = Closure::Cast(obj);
|
|
const Function& func = Function::Handle(Z, closure.function());
|
|
return func.IsImplicitClosureFunction();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsTypedData(Dart_Handle handle) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
intptr_t cid = Api::ClassId(handle);
|
|
return IsTypedDataClassId(cid) || IsExternalTypedDataClassId(cid) ||
|
|
IsTypedDataViewClassId(cid);
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsByteBuffer(Dart_Handle handle) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
return Api::ClassId(handle) == kByteBufferCid;
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsFuture(Dart_Handle handle) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(handle));
|
|
if (obj.IsInstance()) {
|
|
ObjectStore* object_store = T->isolate_group()->object_store();
|
|
const Type& future_rare_type =
|
|
Type::Handle(Z, object_store->non_nullable_future_rare_type());
|
|
ASSERT(!future_rare_type.IsNull());
|
|
const Class& obj_class = Class::Handle(Z, obj.clazz());
|
|
bool is_future = Class::IsSubtypeOf(
|
|
obj_class, Object::null_type_arguments(), Nullability::kNonNullable,
|
|
future_rare_type, Heap::kNew);
|
|
return is_future;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// --- Instances ----
|
|
|
|
DART_EXPORT Dart_Handle Dart_InstanceGetType(Dart_Handle instance) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
auto isolate_group = T->isolate_group();
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(instance));
|
|
if (obj.IsNull()) {
|
|
return Api::NewHandle(T, isolate_group->object_store()->null_type());
|
|
}
|
|
if (!obj.IsInstance()) {
|
|
RETURN_TYPE_ERROR(Z, instance, Instance);
|
|
}
|
|
const AbstractType& type =
|
|
AbstractType::Handle(Instance::Cast(obj).GetType(Heap::kNew));
|
|
return Api::NewHandle(T, type.Canonicalize(T, nullptr));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_FunctionName(Dart_Handle function) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Function& func = Api::UnwrapFunctionHandle(Z, function);
|
|
if (func.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, function, Function);
|
|
}
|
|
return Api::NewHandle(T, func.UserVisibleName());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_ClassName(Dart_Handle cls_type) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Type& type_obj = Api::UnwrapTypeHandle(Z, cls_type);
|
|
if (type_obj.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, cls_type, Type);
|
|
}
|
|
const Class& klass = Class::Handle(Z, type_obj.type_class());
|
|
if (klass.IsNull()) {
|
|
return Api::NewError(
|
|
"cls_type must be a Type object which represents a Class.");
|
|
}
|
|
return Api::NewHandle(T, klass.UserVisibleName());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_FunctionOwner(Dart_Handle function) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Function& func = Api::UnwrapFunctionHandle(Z, function);
|
|
if (func.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, function, Function);
|
|
}
|
|
if (func.IsNonImplicitClosureFunction()) {
|
|
FunctionPtr parent_function = func.parent_function();
|
|
return Api::NewHandle(T, parent_function);
|
|
}
|
|
const Class& owner = Class::Handle(Z, func.Owner());
|
|
ASSERT(!owner.IsNull());
|
|
if (owner.IsTopLevel()) {
|
|
// Top-level functions are implemented as members of a hidden class. We hide
|
|
// that class here and instead answer the library.
|
|
#if defined(DEBUG)
|
|
const Library& lib = Library::Handle(Z, owner.library());
|
|
if (lib.IsNull()) {
|
|
ASSERT(owner.IsDynamicClass() || owner.IsVoidClass() ||
|
|
owner.IsNeverClass());
|
|
}
|
|
#endif
|
|
return Api::NewHandle(T, owner.library());
|
|
} else {
|
|
return Api::NewHandle(T, owner.RareType());
|
|
}
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_FunctionIsStatic(Dart_Handle function,
|
|
bool* is_static) {
|
|
DARTSCOPE(Thread::Current());
|
|
if (is_static == NULL) {
|
|
RETURN_NULL_ERROR(is_static);
|
|
}
|
|
const Function& func = Api::UnwrapFunctionHandle(Z, function);
|
|
if (func.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, function, Function);
|
|
}
|
|
*is_static = func.is_static();
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_ClosureFunction(Dart_Handle closure) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Instance& closure_obj = Api::UnwrapInstanceHandle(Z, closure);
|
|
if (closure_obj.IsNull() || !closure_obj.IsClosure()) {
|
|
RETURN_TYPE_ERROR(Z, closure, Instance);
|
|
}
|
|
|
|
ASSERT(ClassFinalizer::AllClassesFinalized());
|
|
|
|
FunctionPtr rf = Closure::Cast(closure_obj).function();
|
|
return Api::NewHandle(T, rf);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_ClassLibrary(Dart_Handle cls_type) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Type& type_obj = Api::UnwrapTypeHandle(Z, cls_type);
|
|
const Class& klass = Class::Handle(Z, type_obj.type_class());
|
|
if (klass.IsNull()) {
|
|
return Api::NewError(
|
|
"cls_type must be a Type object which represents a Class.");
|
|
}
|
|
const Library& library = Library::Handle(klass.library());
|
|
if (library.IsNull()) {
|
|
return Dart_Null();
|
|
}
|
|
return Api::NewHandle(Thread::Current(), library.ptr());
|
|
}
|
|
|
|
// --- Numbers, Integers and Doubles ----
|
|
|
|
DART_EXPORT Dart_Handle Dart_IntegerFitsIntoInt64(Dart_Handle integer,
|
|
bool* fits) {
|
|
// Fast path for Smis and Mints.
|
|
Thread* thread = Thread::Current();
|
|
API_TIMELINE_DURATION(thread);
|
|
Isolate* isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
if (Api::IsSmi(integer)) {
|
|
*fits = true;
|
|
return Api::Success();
|
|
}
|
|
// Slow path for mints and type error.
|
|
DARTSCOPE(thread);
|
|
if (Api::ClassId(integer) == kMintCid) {
|
|
*fits = true;
|
|
return Api::Success();
|
|
}
|
|
const Integer& int_obj = Api::UnwrapIntegerHandle(Z, integer);
|
|
ASSERT(int_obj.IsNull());
|
|
RETURN_TYPE_ERROR(Z, integer, Integer);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_IntegerFitsIntoUint64(Dart_Handle integer,
|
|
bool* fits) {
|
|
// Fast path for Smis.
|
|
Thread* thread = Thread::Current();
|
|
Isolate* isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
API_TIMELINE_DURATION(thread);
|
|
if (Api::IsSmi(integer)) {
|
|
*fits = (Api::SmiValue(integer) >= 0);
|
|
return Api::Success();
|
|
}
|
|
// Slow path for Mints.
|
|
DARTSCOPE(thread);
|
|
const Integer& int_obj = Api::UnwrapIntegerHandle(Z, integer);
|
|
if (int_obj.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, integer, Integer);
|
|
}
|
|
ASSERT(int_obj.IsMint());
|
|
*fits = !int_obj.IsNegative();
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewInteger(int64_t value) {
|
|
// Fast path for Smis.
|
|
Thread* thread = Thread::Current();
|
|
Isolate* isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
API_TIMELINE_DURATION(thread);
|
|
DARTSCOPE(thread);
|
|
CHECK_CALLBACK_STATE(thread);
|
|
return Api::NewHandle(thread, Integer::New(value));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewIntegerFromUint64(uint64_t value) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
API_TIMELINE_DURATION(T);
|
|
if (Integer::IsValueInRange(value)) {
|
|
return Api::NewHandle(T, Integer::NewFromUint64(value));
|
|
}
|
|
return Api::NewError("%s: Cannot create Dart integer from value %" Pu64,
|
|
CURRENT_FUNC, value);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewIntegerFromHexCString(const char* str) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
API_TIMELINE_DURATION(T);
|
|
const String& str_obj = String::Handle(Z, String::New(str));
|
|
IntegerPtr integer = Integer::New(str_obj);
|
|
if (integer == Integer::null()) {
|
|
return Api::NewError("%s: Cannot create Dart integer from string %s",
|
|
CURRENT_FUNC, str);
|
|
}
|
|
return Api::NewHandle(T, integer);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_IntegerToInt64(Dart_Handle integer,
|
|
int64_t* value) {
|
|
// Fast path for Smis.
|
|
Thread* thread = Thread::Current();
|
|
Isolate* isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
if (Api::IsSmi(integer)) {
|
|
*value = Api::SmiValue(integer);
|
|
return Api::Success();
|
|
}
|
|
// Slow path for Mints.
|
|
DARTSCOPE(thread);
|
|
const Integer& int_obj = Api::UnwrapIntegerHandle(Z, integer);
|
|
if (int_obj.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, integer, Integer);
|
|
}
|
|
ASSERT(int_obj.IsMint());
|
|
*value = int_obj.AsInt64Value();
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_IntegerToUint64(Dart_Handle integer,
|
|
uint64_t* value) {
|
|
// Fast path for Smis.
|
|
Thread* thread = Thread::Current();
|
|
Isolate* isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
if (Api::IsSmi(integer)) {
|
|
intptr_t smi_value = Api::SmiValue(integer);
|
|
if (smi_value >= 0) {
|
|
*value = smi_value;
|
|
return Api::Success();
|
|
}
|
|
}
|
|
// Slow path for Mints.
|
|
DARTSCOPE(thread);
|
|
const Integer& int_obj = Api::UnwrapIntegerHandle(Z, integer);
|
|
if (int_obj.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, integer, Integer);
|
|
}
|
|
if (int_obj.IsSmi()) {
|
|
ASSERT(int_obj.IsNegative());
|
|
} else {
|
|
ASSERT(int_obj.IsMint());
|
|
if (!int_obj.IsNegative()) {
|
|
*value = int_obj.AsInt64Value();
|
|
return Api::Success();
|
|
}
|
|
}
|
|
return Api::NewError("%s: Integer %s cannot be represented as a uint64_t.",
|
|
CURRENT_FUNC, int_obj.ToCString());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_IntegerToHexCString(Dart_Handle integer,
|
|
const char** value) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
const Integer& int_obj = Api::UnwrapIntegerHandle(Z, integer);
|
|
if (int_obj.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, integer, Integer);
|
|
}
|
|
Zone* scope_zone = Api::TopScope(Thread::Current())->zone();
|
|
*value = int_obj.ToHexCString(scope_zone);
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewDouble(double value) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
return Api::NewHandle(T, Double::New(value));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_DoubleValue(Dart_Handle double_obj,
|
|
double* value) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Double& obj = Api::UnwrapDoubleHandle(Z, double_obj);
|
|
if (obj.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, double_obj, Double);
|
|
}
|
|
*value = obj.value();
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetStaticMethodClosure(Dart_Handle library,
|
|
Dart_Handle cls_type,
|
|
Dart_Handle function_name) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Library& lib = Api::UnwrapLibraryHandle(Z, library);
|
|
if (lib.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, library, Library);
|
|
}
|
|
|
|
const Type& type_obj = Api::UnwrapTypeHandle(Z, cls_type);
|
|
if (type_obj.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, cls_type, Type);
|
|
}
|
|
|
|
const Class& klass = Class::Handle(Z, type_obj.type_class());
|
|
if (klass.IsNull()) {
|
|
return Api::NewError(
|
|
"cls_type must be a Type object which represents a Class");
|
|
}
|
|
|
|
const auto& error = klass.EnsureIsFinalized(Thread::Current());
|
|
if (error != Error::null()) {
|
|
return Api::NewHandle(T, error);
|
|
}
|
|
|
|
const String& func_name = Api::UnwrapStringHandle(Z, function_name);
|
|
if (func_name.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, function_name, String);
|
|
}
|
|
|
|
Function& func =
|
|
Function::Handle(Z, klass.LookupStaticFunctionAllowPrivate(func_name));
|
|
if (func.IsNull()) {
|
|
return Dart_Null();
|
|
}
|
|
|
|
if (!func.is_static()) {
|
|
return Api::NewError("function_name must refer to a static method.");
|
|
}
|
|
|
|
if (func.kind() != UntaggedFunction::kRegularFunction) {
|
|
return Api::NewError(
|
|
"function_name must be the name of a regular function.");
|
|
}
|
|
func = func.ImplicitClosureFunction();
|
|
if (func.IsNull()) {
|
|
return Dart_Null();
|
|
}
|
|
|
|
return Api::NewHandle(T, func.ImplicitStaticClosure());
|
|
}
|
|
|
|
// --- Booleans ----
|
|
|
|
DART_EXPORT Dart_Handle Dart_True() {
|
|
ASSERT(Isolate::Current() != NULL);
|
|
return Api::True();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_False() {
|
|
ASSERT(Isolate::Current() != NULL);
|
|
return Api::False();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewBoolean(bool value) {
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
return value ? Api::True() : Api::False();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_BooleanValue(Dart_Handle boolean_obj,
|
|
bool* value) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Bool& obj = Api::UnwrapBoolHandle(Z, boolean_obj);
|
|
if (obj.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, boolean_obj, Bool);
|
|
}
|
|
*value = obj.value();
|
|
return Api::Success();
|
|
}
|
|
|
|
// --- Strings ---
|
|
|
|
DART_EXPORT Dart_Handle Dart_StringLength(Dart_Handle str, intptr_t* len) {
|
|
Thread* thread = Thread::Current();
|
|
DARTSCOPE(thread);
|
|
{
|
|
ReusableObjectHandleScope reused_obj_handle(thread);
|
|
const String& str_obj = Api::UnwrapStringHandle(reused_obj_handle, str);
|
|
if (!str_obj.IsNull()) {
|
|
*len = str_obj.Length();
|
|
return Api::Success();
|
|
}
|
|
}
|
|
RETURN_TYPE_ERROR(thread->zone(), str, String);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewStringFromCString(const char* str) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
if (str == NULL) {
|
|
RETURN_NULL_ERROR(str);
|
|
}
|
|
CHECK_CALLBACK_STATE(T);
|
|
return Api::NewHandle(T, String::New(str));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewStringFromUTF8(const uint8_t* utf8_array,
|
|
intptr_t length) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
if (utf8_array == NULL && length != 0) {
|
|
RETURN_NULL_ERROR(utf8_array);
|
|
}
|
|
CHECK_LENGTH(length, String::kMaxElements);
|
|
if (!Utf8::IsValid(utf8_array, length)) {
|
|
return Api::NewError("%s expects argument 'str' to be valid UTF-8.",
|
|
CURRENT_FUNC);
|
|
}
|
|
CHECK_CALLBACK_STATE(T);
|
|
return Api::NewHandle(T, String::FromUTF8(utf8_array, length));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewStringFromUTF16(const uint16_t* utf16_array,
|
|
intptr_t length) {
|
|
DARTSCOPE(Thread::Current());
|
|
if (utf16_array == NULL && length != 0) {
|
|
RETURN_NULL_ERROR(utf16_array);
|
|
}
|
|
CHECK_LENGTH(length, String::kMaxElements);
|
|
CHECK_CALLBACK_STATE(T);
|
|
return Api::NewHandle(T, String::FromUTF16(utf16_array, length));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewStringFromUTF32(const int32_t* utf32_array,
|
|
intptr_t length) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
if (utf32_array == NULL && length != 0) {
|
|
RETURN_NULL_ERROR(utf32_array);
|
|
}
|
|
CHECK_LENGTH(length, String::kMaxElements);
|
|
CHECK_CALLBACK_STATE(T);
|
|
return Api::NewHandle(T, String::FromUTF32(utf32_array, length));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_NewExternalLatin1String(const uint8_t* latin1_array,
|
|
intptr_t length,
|
|
void* peer,
|
|
intptr_t external_allocation_size,
|
|
Dart_HandleFinalizer callback) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
if (latin1_array == NULL && length != 0) {
|
|
RETURN_NULL_ERROR(latin1_array);
|
|
}
|
|
if (callback == NULL) {
|
|
RETURN_NULL_ERROR(callback);
|
|
}
|
|
CHECK_LENGTH(length, String::kMaxElements);
|
|
CHECK_CALLBACK_STATE(T);
|
|
return Api::NewHandle(
|
|
T,
|
|
String::NewExternal(latin1_array, length, peer, external_allocation_size,
|
|
callback, T->heap()->SpaceForExternal(length)));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_NewExternalUTF16String(const uint16_t* utf16_array,
|
|
intptr_t length,
|
|
void* peer,
|
|
intptr_t external_allocation_size,
|
|
Dart_HandleFinalizer callback) {
|
|
DARTSCOPE(Thread::Current());
|
|
if (utf16_array == NULL && length != 0) {
|
|
RETURN_NULL_ERROR(utf16_array);
|
|
}
|
|
if (callback == NULL) {
|
|
RETURN_NULL_ERROR(callback);
|
|
}
|
|
CHECK_LENGTH(length, String::kMaxElements);
|
|
CHECK_CALLBACK_STATE(T);
|
|
intptr_t bytes = length * sizeof(*utf16_array);
|
|
return Api::NewHandle(
|
|
T,
|
|
String::NewExternal(utf16_array, length, peer, external_allocation_size,
|
|
callback, T->heap()->SpaceForExternal(bytes)));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_StringToCString(Dart_Handle object,
|
|
const char** cstr) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
if (cstr == NULL) {
|
|
RETURN_NULL_ERROR(cstr);
|
|
}
|
|
const String& str_obj = Api::UnwrapStringHandle(Z, object);
|
|
if (str_obj.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, object, String);
|
|
}
|
|
intptr_t string_length = Utf8::Length(str_obj);
|
|
char* res = Api::TopScope(T)->zone()->Alloc<char>(string_length + 1);
|
|
if (res == NULL) {
|
|
return Api::NewError("Unable to allocate memory");
|
|
}
|
|
const char* string_value = str_obj.ToCString();
|
|
memmove(res, string_value, string_length + 1);
|
|
ASSERT(res[string_length] == '\0');
|
|
*cstr = res;
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_StringToUTF8(Dart_Handle str,
|
|
uint8_t** utf8_array,
|
|
intptr_t* length) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
if (utf8_array == NULL) {
|
|
RETURN_NULL_ERROR(utf8_array);
|
|
}
|
|
if (length == NULL) {
|
|
RETURN_NULL_ERROR(length);
|
|
}
|
|
const String& str_obj = Api::UnwrapStringHandle(Z, str);
|
|
if (str_obj.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, str, String);
|
|
}
|
|
intptr_t str_len = Utf8::Length(str_obj);
|
|
*utf8_array = Api::TopScope(T)->zone()->Alloc<uint8_t>(str_len);
|
|
if (*utf8_array == NULL) {
|
|
return Api::NewError("Unable to allocate memory");
|
|
}
|
|
str_obj.ToUTF8(*utf8_array, str_len);
|
|
*length = str_len;
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_StringToLatin1(Dart_Handle str,
|
|
uint8_t* latin1_array,
|
|
intptr_t* length) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
if (latin1_array == NULL) {
|
|
RETURN_NULL_ERROR(latin1_array);
|
|
}
|
|
if (length == NULL) {
|
|
RETURN_NULL_ERROR(length);
|
|
}
|
|
const String& str_obj = Api::UnwrapStringHandle(Z, str);
|
|
if (str_obj.IsNull() || !str_obj.IsOneByteString()) {
|
|
RETURN_TYPE_ERROR(Z, str, String);
|
|
}
|
|
intptr_t str_len = str_obj.Length();
|
|
intptr_t copy_len = (str_len > *length) ? *length : str_len;
|
|
|
|
// We have already asserted that the string object is a Latin-1 string
|
|
// so we can copy the characters over using a simple loop.
|
|
for (intptr_t i = 0; i < copy_len; i++) {
|
|
latin1_array[i] = str_obj.CharAt(i);
|
|
}
|
|
*length = copy_len;
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_StringToUTF16(Dart_Handle str,
|
|
uint16_t* utf16_array,
|
|
intptr_t* length) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
const String& str_obj = Api::UnwrapStringHandle(Z, str);
|
|
if (str_obj.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, str, String);
|
|
}
|
|
intptr_t str_len = str_obj.Length();
|
|
intptr_t copy_len = (str_len > *length) ? *length : str_len;
|
|
for (intptr_t i = 0; i < copy_len; i++) {
|
|
utf16_array[i] = str_obj.CharAt(i);
|
|
}
|
|
*length = copy_len;
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_StringStorageSize(Dart_Handle str,
|
|
intptr_t* size) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
if (size == NULL) {
|
|
RETURN_NULL_ERROR(size);
|
|
}
|
|
{
|
|
ReusableObjectHandleScope reused_obj_handle(thread);
|
|
const String& str_obj = Api::UnwrapStringHandle(reused_obj_handle, str);
|
|
if (!str_obj.IsNull()) {
|
|
*size = (str_obj.Length() * str_obj.CharSize());
|
|
return Api::Success();
|
|
}
|
|
}
|
|
RETURN_TYPE_ERROR(thread->zone(), str, String);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_StringGetProperties(Dart_Handle object,
|
|
intptr_t* char_size,
|
|
intptr_t* str_len,
|
|
void** peer) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
{
|
|
ReusableObjectHandleScope reused_obj_handle(thread);
|
|
const String& str = Api::UnwrapStringHandle(reused_obj_handle, object);
|
|
if (!str.IsNull()) {
|
|
if (str.IsExternal()) {
|
|
*peer = str.GetPeer();
|
|
ASSERT(*peer != NULL);
|
|
} else {
|
|
NoSafepointScope no_safepoint_scope;
|
|
*peer = thread->heap()->GetPeer(str.ptr());
|
|
}
|
|
*char_size = str.CharSize();
|
|
*str_len = str.Length();
|
|
return Api::Success();
|
|
}
|
|
}
|
|
RETURN_TYPE_ERROR(thread->zone(), object, String);
|
|
}
|
|
|
|
// --- Lists ---
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewList(intptr_t length) {
|
|
return Dart_NewListOf(Dart_CoreType_Dynamic, length);
|
|
}
|
|
|
|
static TypeArgumentsPtr TypeArgumentsForElementType(
|
|
ObjectStore* store,
|
|
Dart_CoreType_Id element_type_id) {
|
|
switch (element_type_id) {
|
|
case Dart_CoreType_Dynamic:
|
|
return TypeArguments::null();
|
|
case Dart_CoreType_Int:
|
|
return store->type_argument_legacy_int();
|
|
case Dart_CoreType_String:
|
|
return store->type_argument_legacy_string();
|
|
}
|
|
UNREACHABLE();
|
|
return TypeArguments::null();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewListOf(Dart_CoreType_Id element_type_id,
|
|
intptr_t length) {
|
|
DARTSCOPE(Thread::Current());
|
|
if (T->isolate_group()->null_safety() &&
|
|
element_type_id != Dart_CoreType_Dynamic) {
|
|
return Api::NewError(
|
|
"Cannot use legacy types with --sound-null-safety enabled. "
|
|
"Use Dart_NewListOfType or Dart_NewListOfTypeFilled instead.");
|
|
}
|
|
CHECK_LENGTH(length, Array::kMaxElements);
|
|
CHECK_CALLBACK_STATE(T);
|
|
const Array& arr = Array::Handle(Z, Array::New(length));
|
|
if (element_type_id != Dart_CoreType_Dynamic) {
|
|
arr.SetTypeArguments(TypeArguments::Handle(
|
|
Z, TypeArgumentsForElementType(T->isolate_group()->object_store(),
|
|
element_type_id)));
|
|
}
|
|
return Api::NewHandle(T, arr.ptr());
|
|
}
|
|
|
|
static bool CanTypeContainNull(const Type& type) {
|
|
return (type.nullability() == Nullability::kLegacy) ||
|
|
(type.nullability() == Nullability::kNullable);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewListOfType(Dart_Handle element_type,
|
|
intptr_t length) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_LENGTH(length, Array::kMaxElements);
|
|
CHECK_CALLBACK_STATE(T);
|
|
const Type& type = Api::UnwrapTypeHandle(Z, element_type);
|
|
if (type.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, element_type, Type);
|
|
}
|
|
if (!type.IsFinalized()) {
|
|
return Api::NewError(
|
|
"%s expects argument 'type' to be a fully resolved type.",
|
|
CURRENT_FUNC);
|
|
}
|
|
if ((length > 0) && !CanTypeContainNull(type)) {
|
|
return Api::NewError("%s expects argument 'type' to be a nullable type.",
|
|
CURRENT_FUNC);
|
|
}
|
|
return Api::NewHandle(T, Array::New(length, type));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewListOfTypeFilled(Dart_Handle element_type,
|
|
Dart_Handle fill_object,
|
|
intptr_t length) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_LENGTH(length, Array::kMaxElements);
|
|
CHECK_CALLBACK_STATE(T);
|
|
const Type& type = Api::UnwrapTypeHandle(Z, element_type);
|
|
if (type.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, element_type, Type);
|
|
}
|
|
if (!type.IsFinalized()) {
|
|
return Api::NewError(
|
|
"%s expects argument 'type' to be a fully resolved type.",
|
|
CURRENT_FUNC);
|
|
}
|
|
const Instance& instance = Api::UnwrapInstanceHandle(Z, fill_object);
|
|
if (!instance.IsNull() && !InstanceIsType(T, instance, type)) {
|
|
return Api::NewError(
|
|
"%s expects argument 'fill_object' to have the same type as "
|
|
"'element_type'.",
|
|
CURRENT_FUNC);
|
|
}
|
|
if ((length > 0) && instance.IsNull() && !CanTypeContainNull(type)) {
|
|
return Api::NewError(
|
|
"%s expects argument 'fill_object' to be non-null for a non-nullable "
|
|
"'element_type'.",
|
|
CURRENT_FUNC);
|
|
}
|
|
Array& arr = Array::Handle(Z, Array::New(length, type));
|
|
for (intptr_t i = 0; i < arr.Length(); ++i) {
|
|
arr.SetAt(i, instance);
|
|
}
|
|
return Api::NewHandle(T, arr.ptr());
|
|
}
|
|
|
|
#define GET_LIST_LENGTH(zone, type, obj, len) \
|
|
type& array = type::Handle(zone); \
|
|
array ^= obj.ptr(); \
|
|
*len = array.Length(); \
|
|
return Api::Success();
|
|
|
|
DART_EXPORT Dart_Handle Dart_ListLength(Dart_Handle list, intptr_t* len) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(list));
|
|
if (obj.IsError()) {
|
|
// Pass through errors.
|
|
return list;
|
|
}
|
|
if (obj.IsTypedDataBase()) {
|
|
GET_LIST_LENGTH(Z, TypedDataBase, obj, len);
|
|
}
|
|
if (obj.IsArray()) {
|
|
GET_LIST_LENGTH(Z, Array, obj, len);
|
|
}
|
|
if (obj.IsGrowableObjectArray()) {
|
|
GET_LIST_LENGTH(Z, GrowableObjectArray, obj, len);
|
|
}
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
// Now check and handle a dart object that implements the List interface.
|
|
const Instance& instance = Instance::Handle(Z, GetListInstance(Z, obj));
|
|
if (instance.IsNull()) {
|
|
return Api::NewArgumentError(
|
|
"Object does not implement the List interface");
|
|
}
|
|
const Object& retval =
|
|
Object::Handle(Z, CallStatic1Arg(Z, Symbols::_listLength(), instance));
|
|
if (retval.IsSmi()) {
|
|
*len = Smi::Cast(retval).Value();
|
|
return Api::Success();
|
|
} else if (retval.IsMint()) {
|
|
int64_t mint_value = Mint::Cast(retval).value();
|
|
if (mint_value >= kIntptrMin && mint_value <= kIntptrMax) {
|
|
*len = static_cast<intptr_t>(mint_value);
|
|
return Api::Success();
|
|
}
|
|
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(T, retval.ptr());
|
|
} else {
|
|
return Api::NewError("Length of List object is not an integer");
|
|
}
|
|
}
|
|
|
|
#define GET_LIST_ELEMENT(thread, type, obj, index) \
|
|
const type& array_obj = type::Cast(obj); \
|
|
if ((index >= 0) && (index < array_obj.Length())) { \
|
|
return Api::NewHandle(thread, array_obj.At(index)); \
|
|
} \
|
|
return Api::NewError("Invalid index passed in to access list element");
|
|
|
|
DART_EXPORT Dart_Handle Dart_ListGetAt(Dart_Handle list, intptr_t index) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(list));
|
|
if (obj.IsArray()) {
|
|
GET_LIST_ELEMENT(T, Array, obj, index);
|
|
} else if (obj.IsGrowableObjectArray()) {
|
|
GET_LIST_ELEMENT(T, GrowableObjectArray, obj, index);
|
|
} else if (obj.IsError()) {
|
|
return list;
|
|
} else {
|
|
CHECK_CALLBACK_STATE(T);
|
|
// Check and handle a dart object that implements the List interface.
|
|
const Instance& instance = Instance::Handle(Z, GetListInstance(Z, obj));
|
|
if (!instance.IsNull()) {
|
|
return Api::NewHandle(
|
|
T, CallStatic2Args(Z, Symbols::_listGetAt(), instance,
|
|
Instance::Handle(Z, Integer::New(index))));
|
|
}
|
|
return Api::NewArgumentError(
|
|
"Object does not implement the 'List' interface");
|
|
}
|
|
}
|
|
|
|
#define GET_LIST_RANGE(thread, type, obj, offset, length) \
|
|
const type& array_obj = type::Cast(obj); \
|
|
if ((offset >= 0) && (offset + length <= array_obj.Length())) { \
|
|
for (intptr_t index = 0; index < length; ++index) { \
|
|
result[index] = Api::NewHandle(thread, array_obj.At(index + offset)); \
|
|
} \
|
|
return Api::Success(); \
|
|
} \
|
|
return Api::NewError("Invalid offset/length passed in to access list");
|
|
|
|
DART_EXPORT Dart_Handle Dart_ListGetRange(Dart_Handle list,
|
|
intptr_t offset,
|
|
intptr_t length,
|
|
Dart_Handle* result) {
|
|
DARTSCOPE(Thread::Current());
|
|
if (result == NULL) {
|
|
RETURN_NULL_ERROR(result);
|
|
}
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(list));
|
|
if (obj.IsArray()) {
|
|
GET_LIST_RANGE(T, Array, obj, offset, length);
|
|
} else if (obj.IsGrowableObjectArray()) {
|
|
GET_LIST_RANGE(T, GrowableObjectArray, obj, offset, length);
|
|
} else if (obj.IsError()) {
|
|
return list;
|
|
} else {
|
|
CHECK_CALLBACK_STATE(T);
|
|
// Check and handle a dart object that implements the List interface.
|
|
const Instance& instance = Instance::Handle(Z, GetListInstance(Z, obj));
|
|
if (!instance.IsNull()) {
|
|
const intptr_t kNumArgs = 2;
|
|
const Function& function = Function::Handle(
|
|
Z, FindCoreLibPrivateFunction(Z, Symbols::_listGetAt()));
|
|
const Array& args = Array::Handle(Z, Array::New(kNumArgs));
|
|
args.SetAt(0, instance);
|
|
Instance& index = Instance::Handle(Z);
|
|
for (intptr_t i = 0; i < length; ++i) {
|
|
index = Integer::New(i);
|
|
args.SetAt(1, index);
|
|
Dart_Handle value =
|
|
Api::NewHandle(T, DartEntry::InvokeFunction(function, args));
|
|
if (Api::IsError(value)) return value;
|
|
result[i] = value;
|
|
}
|
|
return Api::Success();
|
|
}
|
|
return Api::NewArgumentError(
|
|
"Object does not implement the 'List' interface");
|
|
}
|
|
}
|
|
|
|
#define SET_LIST_ELEMENT(type, obj, index, value) \
|
|
const type& array = type::Cast(obj); \
|
|
const Object& value_obj = Object::Handle(Z, Api::UnwrapHandle(value)); \
|
|
if (!value_obj.IsNull() && !value_obj.IsInstance()) { \
|
|
RETURN_TYPE_ERROR(Z, value, Instance); \
|
|
} \
|
|
if ((index >= 0) && (index < array.Length())) { \
|
|
array.SetAt(index, value_obj); \
|
|
return Api::Success(); \
|
|
} \
|
|
return Api::NewError("Invalid index passed in to set list element");
|
|
|
|
DART_EXPORT Dart_Handle Dart_ListSetAt(Dart_Handle list,
|
|
intptr_t index,
|
|
Dart_Handle value) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(list));
|
|
// If the list is immutable we call into Dart for the indexed setter to
|
|
// get the unsupported operation exception as the result.
|
|
if (obj.IsArray() && !Array::Cast(obj).IsImmutable()) {
|
|
SET_LIST_ELEMENT(Array, obj, index, value);
|
|
} else if (obj.IsGrowableObjectArray()) {
|
|
SET_LIST_ELEMENT(GrowableObjectArray, obj, index, value);
|
|
} else if (obj.IsError()) {
|
|
return list;
|
|
} else {
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
// Check and handle a dart object that implements the List interface.
|
|
const Instance& instance = Instance::Handle(Z, GetListInstance(Z, obj));
|
|
if (!instance.IsNull()) {
|
|
const Integer& index_obj = Integer::Handle(Z, Integer::New(index));
|
|
const Object& value_obj = Object::Handle(Z, Api::UnwrapHandle(value));
|
|
if (!value_obj.IsNull() && !value_obj.IsInstance()) {
|
|
RETURN_TYPE_ERROR(Z, value, Instance);
|
|
}
|
|
return Api::NewHandle(
|
|
T, CallStatic3Args(Z, Symbols::_listSetAt(), instance, index_obj,
|
|
Instance::Cast(value_obj)));
|
|
}
|
|
return Api::NewArgumentError(
|
|
"Object does not implement the 'List' interface");
|
|
}
|
|
}
|
|
|
|
static ObjectPtr ResolveConstructor(const char* current_func,
|
|
const Class& cls,
|
|
const String& class_name,
|
|
const String& dotted_name,
|
|
int num_args);
|
|
|
|
static ObjectPtr ThrowArgumentError(const char* exception_message) {
|
|
Thread* thread = Thread::Current();
|
|
Zone* zone = thread->zone();
|
|
// Lookup the class ArgumentError in dart:core.
|
|
const String& lib_url = String::Handle(String::New("dart:core"));
|
|
const String& class_name = String::Handle(String::New("ArgumentError"));
|
|
const Library& lib =
|
|
Library::Handle(zone, Library::LookupLibrary(thread, lib_url));
|
|
if (lib.IsNull()) {
|
|
const String& message = String::Handle(String::NewFormatted(
|
|
"%s: library '%s' not found.", CURRENT_FUNC, lib_url.ToCString()));
|
|
return ApiError::New(message);
|
|
}
|
|
const Class& cls =
|
|
Class::Handle(zone, lib.LookupClassAllowPrivate(class_name));
|
|
ASSERT(!cls.IsNull());
|
|
Object& result = Object::Handle(zone);
|
|
String& dot_name = String::Handle(String::New("."));
|
|
String& constr_name = String::Handle(String::Concat(class_name, dot_name));
|
|
result = ResolveConstructor(CURRENT_FUNC, cls, class_name, constr_name, 1);
|
|
if (result.IsError()) return result.ptr();
|
|
ASSERT(result.IsFunction());
|
|
Function& constructor = Function::Handle(zone);
|
|
constructor ^= result.ptr();
|
|
if (!constructor.IsGenerativeConstructor()) {
|
|
const String& message = String::Handle(
|
|
String::NewFormatted("%s: class '%s' is not a constructor.",
|
|
CURRENT_FUNC, class_name.ToCString()));
|
|
return ApiError::New(message);
|
|
}
|
|
Instance& exception = Instance::Handle(zone);
|
|
exception = Instance::New(cls);
|
|
const Array& args = Array::Handle(zone, Array::New(2));
|
|
args.SetAt(0, exception);
|
|
args.SetAt(1, String::Handle(String::New(exception_message)));
|
|
result = DartEntry::InvokeFunction(constructor, args);
|
|
if (result.IsError()) return result.ptr();
|
|
ASSERT(result.IsNull());
|
|
|
|
if (thread->top_exit_frame_info() == 0) {
|
|
// There are no dart frames on the stack so it would be illegal to
|
|
// throw an exception here.
|
|
const String& message = String::Handle(
|
|
String::New("No Dart frames on stack, cannot throw exception"));
|
|
return ApiError::New(message);
|
|
}
|
|
// Unwind all the API scopes till the exit frame before throwing an
|
|
// exception.
|
|
const Instance* saved_exception;
|
|
{
|
|
NoSafepointScope no_safepoint;
|
|
InstancePtr raw_exception = exception.ptr();
|
|
thread->UnwindScopes(thread->top_exit_frame_info());
|
|
saved_exception = &Instance::Handle(raw_exception);
|
|
}
|
|
Exceptions::Throw(thread, *saved_exception);
|
|
const String& message =
|
|
String::Handle(String::New("Exception was not thrown, internal error"));
|
|
return ApiError::New(message);
|
|
}
|
|
|
|
// TODO(sgjesse): value should always be smaller then 0xff. Add error handling.
|
|
#define GET_LIST_ELEMENT_AS_BYTES(type, obj, native_array, offset, length) \
|
|
const type& array = type::Cast(obj); \
|
|
if (Utils::RangeCheck(offset, length, array.Length())) { \
|
|
Object& element = Object::Handle(Z); \
|
|
for (int i = 0; i < length; i++) { \
|
|
element = array.At(offset + i); \
|
|
if (!element.IsInteger()) { \
|
|
return Api::NewHandle( \
|
|
T, ThrowArgumentError("List contains non-int elements")); \
|
|
} \
|
|
const Integer& integer = Integer::Cast(element); \
|
|
native_array[i] = static_cast<uint8_t>(integer.AsInt64Value() & 0xff); \
|
|
ASSERT(integer.AsInt64Value() <= 0xff); \
|
|
} \
|
|
return Api::Success(); \
|
|
} \
|
|
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) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(list));
|
|
if (obj.IsTypedDataBase()) {
|
|
const TypedDataBase& array = TypedDataBase::Cast(obj);
|
|
if (array.ElementSizeInBytes() == 1) {
|
|
if (Utils::RangeCheck(offset, length, array.Length())) {
|
|
NoSafepointScope no_safepoint;
|
|
memmove(native_array,
|
|
reinterpret_cast<uint8_t*>(array.DataAddr(offset)), length);
|
|
return Api::Success();
|
|
}
|
|
return Api::NewError("Invalid length passed in to access list elements");
|
|
}
|
|
}
|
|
if (obj.IsArray()) {
|
|
GET_LIST_ELEMENT_AS_BYTES(Array, obj, native_array, offset, length);
|
|
}
|
|
if (obj.IsGrowableObjectArray()) {
|
|
GET_LIST_ELEMENT_AS_BYTES(GrowableObjectArray, obj, native_array, offset,
|
|
length);
|
|
}
|
|
if (obj.IsError()) {
|
|
return list;
|
|
}
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
// Check and handle a dart object that implements the List interface.
|
|
const Instance& instance = Instance::Handle(Z, GetListInstance(Z, obj));
|
|
if (!instance.IsNull()) {
|
|
const int kNumArgs = 2;
|
|
const Function& function = Function::Handle(
|
|
Z, FindCoreLibPrivateFunction(Z, Symbols::_listGetAt()));
|
|
Object& result = Object::Handle(Z);
|
|
Integer& intobj = Integer::Handle(Z);
|
|
const Array& args = Array::Handle(Z, Array::New(kNumArgs));
|
|
args.SetAt(0, instance); // Set up the receiver as the first argument.
|
|
for (int i = 0; i < length; i++) {
|
|
HANDLESCOPE(T);
|
|
intobj = Integer::New(offset + i);
|
|
args.SetAt(1, intobj);
|
|
result = DartEntry::InvokeFunction(function, args);
|
|
if (result.IsError()) {
|
|
return Api::NewHandle(T, result.ptr());
|
|
}
|
|
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<uint8_t>(integer_result.AsInt64Value() & 0xff);
|
|
}
|
|
return Api::Success();
|
|
}
|
|
return Api::NewArgumentError(
|
|
"Object does not implement the 'List' interface");
|
|
}
|
|
|
|
#define SET_LIST_ELEMENT_AS_BYTES(type, obj, native_array, offset, length) \
|
|
const type& array = type::Cast(obj); \
|
|
Integer& integer = Integer::Handle(Z); \
|
|
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(); \
|
|
} \
|
|
return Api::NewError("Invalid length passed in to set array elements");
|
|
|
|
DART_EXPORT Dart_Handle Dart_ListSetAsBytes(Dart_Handle list,
|
|
intptr_t offset,
|
|
const uint8_t* native_array,
|
|
intptr_t length) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(list));
|
|
if (obj.IsTypedDataBase()) {
|
|
const TypedDataBase& array = TypedDataBase::Cast(obj);
|
|
if (array.ElementSizeInBytes() == 1) {
|
|
if (Utils::RangeCheck(offset, length, array.Length())) {
|
|
NoSafepointScope no_safepoint;
|
|
memmove(reinterpret_cast<uint8_t*>(array.DataAddr(offset)),
|
|
native_array, length);
|
|
return Api::Success();
|
|
}
|
|
return Api::NewError("Invalid length passed in to access list elements");
|
|
}
|
|
}
|
|
if (obj.IsArray() && !Array::Cast(obj).IsImmutable()) {
|
|
// 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(Array, obj, native_array, offset, length);
|
|
}
|
|
if (obj.IsGrowableObjectArray()) {
|
|
SET_LIST_ELEMENT_AS_BYTES(GrowableObjectArray, obj, native_array, offset,
|
|
length);
|
|
}
|
|
if (obj.IsError()) {
|
|
return list;
|
|
}
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
// Check and handle a dart object that implements the List interface.
|
|
const Instance& instance = Instance::Handle(Z, GetListInstance(Z, obj));
|
|
if (!instance.IsNull()) {
|
|
const int kNumArgs = 3;
|
|
const Function& function = Function::Handle(
|
|
Z, FindCoreLibPrivateFunction(Z, Symbols::_listSetAt()));
|
|
Integer& indexobj = Integer::Handle(Z);
|
|
Integer& valueobj = Integer::Handle(Z);
|
|
const Array& args = Array::Handle(Z, 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(Z, DartEntry::InvokeFunction(function, args));
|
|
if (result.IsError()) {
|
|
return Api::NewHandle(T, result.ptr());
|
|
}
|
|
}
|
|
return Api::Success();
|
|
}
|
|
return Api::NewArgumentError(
|
|
"Object does not implement the 'List' interface");
|
|
}
|
|
|
|
// --- Maps ---
|
|
|
|
DART_EXPORT Dart_Handle Dart_MapGetAt(Dart_Handle map, Dart_Handle key) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(map));
|
|
const Instance& instance = Instance::Handle(Z, GetMapInstance(Z, obj));
|
|
if (!instance.IsNull()) {
|
|
const Object& key_obj = Object::Handle(Api::UnwrapHandle(key));
|
|
if (!(key_obj.IsInstance() || key_obj.IsNull())) {
|
|
return Api::NewError("Key is not an instance");
|
|
}
|
|
return Api::NewHandle(T, CallStatic2Args(Z, Symbols::_mapGet(), instance,
|
|
Instance::Cast(key_obj)));
|
|
}
|
|
return Api::NewArgumentError("Object does not implement the 'Map' interface");
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_MapContainsKey(Dart_Handle map, Dart_Handle key) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(map));
|
|
const Instance& instance = Instance::Handle(Z, GetMapInstance(Z, obj));
|
|
if (!instance.IsNull()) {
|
|
const Object& key_obj = Object::Handle(Z, Api::UnwrapHandle(key));
|
|
if (!(key_obj.IsInstance() || key_obj.IsNull())) {
|
|
return Api::NewError("Key is not an instance");
|
|
}
|
|
return Api::NewHandle(
|
|
T, CallStatic2Args(Z, Symbols::_mapContainsKey(), instance,
|
|
Instance::Cast(key_obj)));
|
|
}
|
|
return Api::NewArgumentError("Object does not implement the 'Map' interface");
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_MapKeys(Dart_Handle map) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
Object& obj = Object::Handle(Z, Api::UnwrapHandle(map));
|
|
Instance& instance = Instance::Handle(Z, GetMapInstance(Z, obj));
|
|
if (!instance.IsNull()) {
|
|
return Api::NewHandle(T, CallStatic1Arg(Z, Symbols::_mapKeys(), instance));
|
|
}
|
|
return Api::NewArgumentError("Object does not implement the 'Map' interface");
|
|
}
|
|
|
|
// --- Typed Data ---
|
|
|
|
// Helper method to get the type of a TypedData object.
|
|
static Dart_TypedData_Type GetType(intptr_t class_id) {
|
|
Dart_TypedData_Type type;
|
|
switch (class_id) {
|
|
case kByteDataViewCid:
|
|
type = Dart_TypedData_kByteData;
|
|
break;
|
|
case kTypedDataInt8ArrayCid:
|
|
case kTypedDataInt8ArrayViewCid:
|
|
case kExternalTypedDataInt8ArrayCid:
|
|
type = Dart_TypedData_kInt8;
|
|
break;
|
|
case kTypedDataUint8ArrayCid:
|
|
case kTypedDataUint8ArrayViewCid:
|
|
case kExternalTypedDataUint8ArrayCid:
|
|
type = Dart_TypedData_kUint8;
|
|
break;
|
|
case kTypedDataUint8ClampedArrayCid:
|
|
case kTypedDataUint8ClampedArrayViewCid:
|
|
case kExternalTypedDataUint8ClampedArrayCid:
|
|
type = Dart_TypedData_kUint8Clamped;
|
|
break;
|
|
case kTypedDataInt16ArrayCid:
|
|
case kTypedDataInt16ArrayViewCid:
|
|
case kExternalTypedDataInt16ArrayCid:
|
|
type = Dart_TypedData_kInt16;
|
|
break;
|
|
case kTypedDataUint16ArrayCid:
|
|
case kTypedDataUint16ArrayViewCid:
|
|
case kExternalTypedDataUint16ArrayCid:
|
|
type = Dart_TypedData_kUint16;
|
|
break;
|
|
case kTypedDataInt32ArrayCid:
|
|
case kTypedDataInt32ArrayViewCid:
|
|
case kExternalTypedDataInt32ArrayCid:
|
|
type = Dart_TypedData_kInt32;
|
|
break;
|
|
case kTypedDataUint32ArrayCid:
|
|
case kTypedDataUint32ArrayViewCid:
|
|
case kExternalTypedDataUint32ArrayCid:
|
|
type = Dart_TypedData_kUint32;
|
|
break;
|
|
case kTypedDataInt64ArrayCid:
|
|
case kTypedDataInt64ArrayViewCid:
|
|
case kExternalTypedDataInt64ArrayCid:
|
|
type = Dart_TypedData_kInt64;
|
|
break;
|
|
case kTypedDataUint64ArrayCid:
|
|
case kTypedDataUint64ArrayViewCid:
|
|
case kExternalTypedDataUint64ArrayCid:
|
|
type = Dart_TypedData_kUint64;
|
|
break;
|
|
case kTypedDataFloat32ArrayCid:
|
|
case kTypedDataFloat32ArrayViewCid:
|
|
case kExternalTypedDataFloat32ArrayCid:
|
|
type = Dart_TypedData_kFloat32;
|
|
break;
|
|
case kTypedDataFloat64ArrayCid:
|
|
case kTypedDataFloat64ArrayViewCid:
|
|
case kExternalTypedDataFloat64ArrayCid:
|
|
type = Dart_TypedData_kFloat64;
|
|
break;
|
|
case kTypedDataInt32x4ArrayCid:
|
|
case kTypedDataInt32x4ArrayViewCid:
|
|
case kExternalTypedDataInt32x4ArrayCid:
|
|
type = Dart_TypedData_kInt32x4;
|
|
break;
|
|
case kTypedDataFloat32x4ArrayCid:
|
|
case kTypedDataFloat32x4ArrayViewCid:
|
|
case kExternalTypedDataFloat32x4ArrayCid:
|
|
type = Dart_TypedData_kFloat32x4;
|
|
break;
|
|
case kTypedDataFloat64x2ArrayCid:
|
|
case kTypedDataFloat64x2ArrayViewCid:
|
|
case kExternalTypedDataFloat64x2ArrayCid:
|
|
type = Dart_TypedData_kFloat64x2;
|
|
break;
|
|
default:
|
|
type = Dart_TypedData_kInvalid;
|
|
break;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
DART_EXPORT Dart_TypedData_Type Dart_GetTypeOfTypedData(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
API_TIMELINE_DURATION(thread);
|
|
TransitionNativeToVM transition(thread);
|
|
intptr_t class_id = Api::ClassId(object);
|
|
if (IsTypedDataClassId(class_id) || IsTypedDataViewClassId(class_id)) {
|
|
return GetType(class_id);
|
|
}
|
|
return Dart_TypedData_kInvalid;
|
|
}
|
|
|
|
DART_EXPORT Dart_TypedData_Type
|
|
Dart_GetTypeOfExternalTypedData(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
API_TIMELINE_DURATION(thread);
|
|
TransitionNativeToVM transition(thread);
|
|
intptr_t class_id = Api::ClassId(object);
|
|
if (IsExternalTypedDataClassId(class_id)) {
|
|
return GetType(class_id);
|
|
}
|
|
if (IsTypedDataViewClassId(class_id)) {
|
|
// Check if data object of the view is external.
|
|
Zone* zone = thread->zone();
|
|
const auto& view_obj = Api::UnwrapTypedDataViewHandle(zone, object);
|
|
ASSERT(!view_obj.IsNull());
|
|
const auto& data_obj = Instance::Handle(zone, view_obj.typed_data());
|
|
if (ExternalTypedData::IsExternalTypedData(data_obj)) {
|
|
return GetType(class_id);
|
|
}
|
|
}
|
|
return Dart_TypedData_kInvalid;
|
|
}
|
|
|
|
static ObjectPtr GetByteDataConstructor(Thread* thread,
|
|
const String& constructor_name,
|
|
intptr_t num_args) {
|
|
const Library& lib = Library::Handle(
|
|
thread->isolate_group()->object_store()->typed_data_library());
|
|
ASSERT(!lib.IsNull());
|
|
const Class& cls = Class::Handle(
|
|
thread->zone(), lib.LookupClassAllowPrivate(Symbols::ByteData()));
|
|
ASSERT(!cls.IsNull());
|
|
return ResolveConstructor(CURRENT_FUNC, cls, Symbols::ByteData(),
|
|
constructor_name, num_args);
|
|
}
|
|
|
|
static Dart_Handle NewByteData(Thread* thread, intptr_t length) {
|
|
CHECK_LENGTH(length, TypedData::MaxElements(kTypedDataInt8ArrayCid));
|
|
Zone* zone = thread->zone();
|
|
Object& result = Object::Handle(zone);
|
|
result = GetByteDataConstructor(thread, Symbols::ByteDataDot(), 1);
|
|
ASSERT(!result.IsNull());
|
|
ASSERT(result.IsFunction());
|
|
const Function& factory = Function::Cast(result);
|
|
ASSERT(!factory.IsGenerativeConstructor());
|
|
|
|
// Create the argument list.
|
|
const Array& args = Array::Handle(zone, Array::New(2));
|
|
// Factories get type arguments.
|
|
args.SetAt(0, Object::null_type_arguments());
|
|
args.SetAt(1, Smi::Handle(zone, Smi::New(length)));
|
|
|
|
// Invoke the constructor and return the new object.
|
|
result = DartEntry::InvokeFunction(factory, args);
|
|
ASSERT(result.IsInstance() || result.IsNull() || result.IsError());
|
|
return Api::NewHandle(thread, result.ptr());
|
|
}
|
|
|
|
static Dart_Handle NewTypedData(Thread* thread, intptr_t cid, intptr_t length) {
|
|
CHECK_LENGTH(length, TypedData::MaxElements(cid));
|
|
return Api::NewHandle(thread, TypedData::New(cid, length));
|
|
}
|
|
|
|
static Dart_Handle NewExternalTypedData(Thread* thread,
|
|
intptr_t cid,
|
|
void* data,
|
|
intptr_t length,
|
|
void* peer,
|
|
intptr_t external_allocation_size,
|
|
Dart_HandleFinalizer callback) {
|
|
CHECK_LENGTH(length, ExternalTypedData::MaxElements(cid));
|
|
Zone* zone = thread->zone();
|
|
intptr_t bytes = length * ExternalTypedData::ElementSizeInBytes(cid);
|
|
auto& cls =
|
|
Class::Handle(zone, thread->isolate_group()->class_table()->At(cid));
|
|
auto& result = Object::Handle(zone, cls.EnsureIsAllocateFinalized(thread));
|
|
if (result.IsError()) {
|
|
return Api::NewHandle(thread, result.ptr());
|
|
}
|
|
result = ExternalTypedData::New(cid, reinterpret_cast<uint8_t*>(data), length,
|
|
thread->heap()->SpaceForExternal(bytes));
|
|
if (callback != nullptr) {
|
|
AllocateFinalizableHandle(thread, result, peer, external_allocation_size,
|
|
callback);
|
|
}
|
|
return Api::NewHandle(thread, result.ptr());
|
|
}
|
|
|
|
static Dart_Handle NewExternalByteData(Thread* thread,
|
|
void* data,
|
|
intptr_t length,
|
|
void* peer,
|
|
intptr_t external_allocation_size,
|
|
Dart_HandleFinalizer callback) {
|
|
Zone* zone = thread->zone();
|
|
Dart_Handle ext_data =
|
|
NewExternalTypedData(thread, kExternalTypedDataUint8ArrayCid, data,
|
|
length, peer, external_allocation_size, callback);
|
|
if (Api::IsError(ext_data)) {
|
|
return ext_data;
|
|
}
|
|
Object& result = Object::Handle(zone);
|
|
result = GetByteDataConstructor(thread, Symbols::ByteDataDot_view(), 3);
|
|
ASSERT(!result.IsNull());
|
|
ASSERT(result.IsFunction());
|
|
const Function& factory = Function::Cast(result);
|
|
ASSERT(!factory.IsGenerativeConstructor());
|
|
|
|
// Create the argument list.
|
|
const intptr_t num_args = 3;
|
|
const Array& args = Array::Handle(zone, Array::New(num_args + 1));
|
|
// Factories get type arguments.
|
|
args.SetAt(0, Object::null_type_arguments());
|
|
const ExternalTypedData& array =
|
|
Api::UnwrapExternalTypedDataHandle(zone, ext_data);
|
|
args.SetAt(1, array);
|
|
Smi& smi = Smi::Handle(zone);
|
|
smi = Smi::New(0);
|
|
args.SetAt(2, smi);
|
|
smi = Smi::New(length);
|
|
args.SetAt(3, smi);
|
|
|
|
// Invoke the constructor and return the new object.
|
|
result = DartEntry::InvokeFunction(factory, args);
|
|
ASSERT(result.IsNull() || result.IsInstance() || result.IsError());
|
|
return Api::NewHandle(thread, result.ptr());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewTypedData(Dart_TypedData_Type type,
|
|
intptr_t length) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
switch (type) {
|
|
case Dart_TypedData_kByteData:
|
|
return NewByteData(T, length);
|
|
case Dart_TypedData_kInt8:
|
|
return NewTypedData(T, kTypedDataInt8ArrayCid, length);
|
|
case Dart_TypedData_kUint8:
|
|
return NewTypedData(T, kTypedDataUint8ArrayCid, length);
|
|
case Dart_TypedData_kUint8Clamped:
|
|
return NewTypedData(T, kTypedDataUint8ClampedArrayCid, length);
|
|
case Dart_TypedData_kInt16:
|
|
return NewTypedData(T, kTypedDataInt16ArrayCid, length);
|
|
case Dart_TypedData_kUint16:
|
|
return NewTypedData(T, kTypedDataUint16ArrayCid, length);
|
|
case Dart_TypedData_kInt32:
|
|
return NewTypedData(T, kTypedDataInt32ArrayCid, length);
|
|
case Dart_TypedData_kUint32:
|
|
return NewTypedData(T, kTypedDataUint32ArrayCid, length);
|
|
case Dart_TypedData_kInt64:
|
|
return NewTypedData(T, kTypedDataInt64ArrayCid, length);
|
|
case Dart_TypedData_kUint64:
|
|
return NewTypedData(T, kTypedDataUint64ArrayCid, length);
|
|
case Dart_TypedData_kFloat32:
|
|
return NewTypedData(T, kTypedDataFloat32ArrayCid, length);
|
|
case Dart_TypedData_kFloat64:
|
|
return NewTypedData(T, kTypedDataFloat64ArrayCid, length);
|
|
case Dart_TypedData_kInt32x4:
|
|
return NewTypedData(T, kTypedDataInt32x4ArrayCid, length);
|
|
case Dart_TypedData_kFloat32x4:
|
|
return NewTypedData(T, kTypedDataFloat32x4ArrayCid, length);
|
|
case Dart_TypedData_kFloat64x2:
|
|
return NewTypedData(T, kTypedDataFloat64x2ArrayCid, length);
|
|
default:
|
|
return Api::NewError("%s expects argument 'type' to be of 'TypedData'",
|
|
CURRENT_FUNC);
|
|
}
|
|
UNREACHABLE();
|
|
return Api::Null();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewExternalTypedData(Dart_TypedData_Type type,
|
|
void* data,
|
|
intptr_t length) {
|
|
return Dart_NewExternalTypedDataWithFinalizer(type, data, length, NULL, 0,
|
|
NULL);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_NewExternalTypedDataWithFinalizer(Dart_TypedData_Type type,
|
|
void* data,
|
|
intptr_t length,
|
|
void* peer,
|
|
intptr_t external_allocation_size,
|
|
Dart_HandleFinalizer callback) {
|
|
DARTSCOPE(Thread::Current());
|
|
if (data == NULL && length != 0) {
|
|
RETURN_NULL_ERROR(data);
|
|
}
|
|
CHECK_CALLBACK_STATE(T);
|
|
switch (type) {
|
|
case Dart_TypedData_kByteData:
|
|
return NewExternalByteData(T, data, length, peer,
|
|
external_allocation_size, callback);
|
|
case Dart_TypedData_kInt8:
|
|
return NewExternalTypedData(T, kExternalTypedDataInt8ArrayCid, data,
|
|
length, peer, external_allocation_size,
|
|
callback);
|
|
case Dart_TypedData_kUint8:
|
|
return NewExternalTypedData(T, kExternalTypedDataUint8ArrayCid, data,
|
|
length, peer, external_allocation_size,
|
|
callback);
|
|
case Dart_TypedData_kUint8Clamped:
|
|
return NewExternalTypedData(T, kExternalTypedDataUint8ClampedArrayCid,
|
|
data, length, peer, external_allocation_size,
|
|
callback);
|
|
case Dart_TypedData_kInt16:
|
|
return NewExternalTypedData(T, kExternalTypedDataInt16ArrayCid, data,
|
|
length, peer, external_allocation_size,
|
|
callback);
|
|
case Dart_TypedData_kUint16:
|
|
return NewExternalTypedData(T, kExternalTypedDataUint16ArrayCid, data,
|
|
length, peer, external_allocation_size,
|
|
callback);
|
|
case Dart_TypedData_kInt32:
|
|
return NewExternalTypedData(T, kExternalTypedDataInt32ArrayCid, data,
|
|
length, peer, external_allocation_size,
|
|
callback);
|
|
case Dart_TypedData_kUint32:
|
|
return NewExternalTypedData(T, kExternalTypedDataUint32ArrayCid, data,
|
|
length, peer, external_allocation_size,
|
|
callback);
|
|
case Dart_TypedData_kInt64:
|
|
return NewExternalTypedData(T, kExternalTypedDataInt64ArrayCid, data,
|
|
length, peer, external_allocation_size,
|
|
callback);
|
|
case Dart_TypedData_kUint64:
|
|
return NewExternalTypedData(T, kExternalTypedDataUint64ArrayCid, data,
|
|
length, peer, external_allocation_size,
|
|
callback);
|
|
case Dart_TypedData_kFloat32:
|
|
return NewExternalTypedData(T, kExternalTypedDataFloat32ArrayCid, data,
|
|
length, peer, external_allocation_size,
|
|
callback);
|
|
case Dart_TypedData_kFloat64:
|
|
return NewExternalTypedData(T, kExternalTypedDataFloat64ArrayCid, data,
|
|
length, peer, external_allocation_size,
|
|
callback);
|
|
case Dart_TypedData_kInt32x4:
|
|
return NewExternalTypedData(T, kExternalTypedDataInt32x4ArrayCid, data,
|
|
length, peer, external_allocation_size,
|
|
callback);
|
|
case Dart_TypedData_kFloat32x4:
|
|
return NewExternalTypedData(T, kExternalTypedDataFloat32x4ArrayCid, data,
|
|
length, peer, external_allocation_size,
|
|
callback);
|
|
case Dart_TypedData_kFloat64x2:
|
|
return NewExternalTypedData(T, kExternalTypedDataFloat64x2ArrayCid, data,
|
|
length, peer, external_allocation_size,
|
|
callback);
|
|
default:
|
|
return Api::NewError(
|
|
"%s expects argument 'type' to be of"
|
|
" 'external TypedData'",
|
|
CURRENT_FUNC);
|
|
}
|
|
UNREACHABLE();
|
|
return Api::Null();
|
|
}
|
|
|
|
static ObjectPtr GetByteBufferConstructor(Thread* thread,
|
|
const String& class_name,
|
|
const String& constructor_name,
|
|
intptr_t num_args) {
|
|
const Library& lib = Library::Handle(
|
|
thread->isolate_group()->object_store()->typed_data_library());
|
|
ASSERT(!lib.IsNull());
|
|
const Class& cls =
|
|
Class::Handle(thread->zone(), lib.LookupClassAllowPrivate(class_name));
|
|
ASSERT(!cls.IsNull());
|
|
return ResolveConstructor(CURRENT_FUNC, cls, class_name, constructor_name,
|
|
num_args);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewByteBuffer(Dart_Handle typed_data) {
|
|
DARTSCOPE(Thread::Current());
|
|
intptr_t class_id = Api::ClassId(typed_data);
|
|
if (!IsExternalTypedDataClassId(class_id) &&
|
|
!IsTypedDataViewClassId(class_id) && !IsTypedDataClassId(class_id)) {
|
|
RETURN_TYPE_ERROR(Z, typed_data, 'TypedData');
|
|
}
|
|
Object& result = Object::Handle(Z);
|
|
result = GetByteBufferConstructor(T, Symbols::_ByteBuffer(),
|
|
Symbols::_ByteBufferDot_New(), 1);
|
|
ASSERT(!result.IsNull());
|
|
ASSERT(result.IsFunction());
|
|
const Function& factory = Function::Cast(result);
|
|
ASSERT(!factory.IsGenerativeConstructor());
|
|
|
|
// Create the argument list.
|
|
const Array& args = Array::Handle(Z, Array::New(2));
|
|
// Factories get type arguments.
|
|
args.SetAt(0, Object::null_type_arguments());
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(typed_data));
|
|
args.SetAt(1, obj);
|
|
|
|
// Invoke the factory constructor and return the new object.
|
|
result = DartEntry::InvokeFunction(factory, args);
|
|
ASSERT(result.IsInstance() || result.IsNull() || result.IsError());
|
|
return Api::NewHandle(T, result.ptr());
|
|
}
|
|
|
|
// Structure to record acquired typed data for verification purposes.
|
|
class AcquiredData {
|
|
public:
|
|
AcquiredData(void* data, intptr_t size_in_bytes, bool copy)
|
|
: size_in_bytes_(size_in_bytes), data_(data), data_copy_(NULL) {
|
|
if (copy) {
|
|
data_copy_ = malloc(size_in_bytes_);
|
|
memmove(data_copy_, data_, size_in_bytes_);
|
|
}
|
|
}
|
|
|
|
// The pointer to hand out via the API.
|
|
void* GetData() const { return data_copy_ != NULL ? data_copy_ : data_; }
|
|
|
|
// Writes back and deletes/zaps, if a copy was made.
|
|
~AcquiredData() {
|
|
if (data_copy_ != NULL) {
|
|
memmove(data_, data_copy_, size_in_bytes_);
|
|
memset(data_copy_, kZapReleasedByte, size_in_bytes_);
|
|
free(data_copy_);
|
|
}
|
|
}
|
|
|
|
private:
|
|
static const uint8_t kZapReleasedByte = 0xda;
|
|
intptr_t size_in_bytes_;
|
|
void* data_;
|
|
void* data_copy_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(AcquiredData);
|
|
};
|
|
|
|
DART_EXPORT Dart_Handle Dart_TypedDataAcquireData(Dart_Handle object,
|
|
Dart_TypedData_Type* type,
|
|
void** data,
|
|
intptr_t* len) {
|
|
DARTSCOPE(Thread::Current());
|
|
Isolate* I = T->isolate();
|
|
intptr_t class_id = Api::ClassId(object);
|
|
if (!IsExternalTypedDataClassId(class_id) &&
|
|
!IsTypedDataViewClassId(class_id) && !IsTypedDataClassId(class_id)) {
|
|
RETURN_TYPE_ERROR(Z, object, 'TypedData');
|
|
}
|
|
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 data object.
|
|
*type = GetType(class_id);
|
|
intptr_t length = 0;
|
|
intptr_t size_in_bytes = 0;
|
|
void* data_tmp = NULL;
|
|
bool external = false;
|
|
T->IncrementNoSafepointScopeDepth();
|
|
START_NO_CALLBACK_SCOPE(T);
|
|
if (IsExternalTypedDataClassId(class_id)) {
|
|
const ExternalTypedData& obj =
|
|
Api::UnwrapExternalTypedDataHandle(Z, object);
|
|
ASSERT(!obj.IsNull());
|
|
length = obj.Length();
|
|
size_in_bytes = length * ExternalTypedData::ElementSizeInBytes(class_id);
|
|
data_tmp = obj.DataAddr(0);
|
|
external = true;
|
|
} else if (IsTypedDataClassId(class_id)) {
|
|
const TypedData& obj = Api::UnwrapTypedDataHandle(Z, object);
|
|
ASSERT(!obj.IsNull());
|
|
length = obj.Length();
|
|
size_in_bytes = length * TypedData::ElementSizeInBytes(class_id);
|
|
data_tmp = obj.DataAddr(0);
|
|
} else {
|
|
ASSERT(IsTypedDataViewClassId(class_id));
|
|
const auto& view_obj = Api::UnwrapTypedDataViewHandle(Z, object);
|
|
ASSERT(!view_obj.IsNull());
|
|
Smi& val = Smi::Handle();
|
|
val = view_obj.length();
|
|
length = val.Value();
|
|
size_in_bytes = length * TypedDataView::ElementSizeInBytes(class_id);
|
|
val = view_obj.offset_in_bytes();
|
|
intptr_t offset_in_bytes = val.Value();
|
|
const auto& obj = Instance::Handle(view_obj.typed_data());
|
|
if (TypedData::IsTypedData(obj)) {
|
|
const TypedData& data_obj = TypedData::Cast(obj);
|
|
data_tmp = data_obj.DataAddr(offset_in_bytes);
|
|
} else {
|
|
ASSERT(ExternalTypedData::IsExternalTypedData(obj));
|
|
const ExternalTypedData& data_obj = ExternalTypedData::Cast(obj);
|
|
data_tmp = data_obj.DataAddr(offset_in_bytes);
|
|
external = true;
|
|
}
|
|
}
|
|
if (FLAG_verify_acquired_data) {
|
|
{
|
|
NoSafepointScope no_safepoint(T);
|
|
bool sweep_in_progress;
|
|
{
|
|
PageSpace* old_space = T->heap()->old_space();
|
|
MonitorLocker ml(old_space->tasks_lock());
|
|
sweep_in_progress = (old_space->phase() == PageSpace::kSweepingLarge) ||
|
|
(old_space->phase() == PageSpace::kSweepingRegular);
|
|
}
|
|
if (!sweep_in_progress) {
|
|
if (external) {
|
|
ASSERT(!T->heap()->Contains(reinterpret_cast<uword>(data_tmp)));
|
|
} else {
|
|
ASSERT(T->heap()->Contains(reinterpret_cast<uword>(data_tmp)));
|
|
}
|
|
}
|
|
}
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(object));
|
|
WeakTable* table = I->group()->api_state()->acquired_table();
|
|
intptr_t current = table->GetValue(obj.ptr());
|
|
if (current != 0) {
|
|
return Api::NewError("Data was already acquired for this object.");
|
|
}
|
|
// Do not make a copy if the data is external. Some callers expect external
|
|
// data to remain in place, even though the API spec doesn't guarantee it.
|
|
// TODO(koda/asiva): Make final decision and document it.
|
|
AcquiredData* ad = new AcquiredData(data_tmp, size_in_bytes, !external);
|
|
table->SetValue(obj.ptr(), reinterpret_cast<intptr_t>(ad));
|
|
data_tmp = ad->GetData();
|
|
}
|
|
*data = data_tmp;
|
|
*len = length;
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_TypedDataReleaseData(Dart_Handle object) {
|
|
DARTSCOPE(Thread::Current());
|
|
Isolate* I = T->isolate();
|
|
intptr_t class_id = Api::ClassId(object);
|
|
if (!IsExternalTypedDataClassId(class_id) &&
|
|
!IsTypedDataViewClassId(class_id) && !IsTypedDataClassId(class_id)) {
|
|
RETURN_TYPE_ERROR(Z, object, 'TypedData');
|
|
}
|
|
if (FLAG_verify_acquired_data) {
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(object));
|
|
WeakTable* table = I->group()->api_state()->acquired_table();
|
|
intptr_t current = table->GetValue(obj.ptr());
|
|
if (current == 0) {
|
|
return Api::NewError("Data was not acquired for this object.");
|
|
}
|
|
AcquiredData* ad = reinterpret_cast<AcquiredData*>(current);
|
|
table->SetValue(obj.ptr(), 0); // Delete entry from table.
|
|
delete ad;
|
|
}
|
|
T->DecrementNoSafepointScopeDepth();
|
|
END_NO_CALLBACK_SCOPE(T);
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetDataFromByteBuffer(Dart_Handle object) {
|
|
Thread* thread = Thread::Current();
|
|
Zone* zone = thread->zone();
|
|
Isolate* isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
TransitionNativeToVM transition(thread);
|
|
intptr_t class_id = Api::ClassId(object);
|
|
if (class_id != kByteBufferCid) {
|
|
RETURN_TYPE_ERROR(zone, object, 'ByteBuffer');
|
|
}
|
|
const Instance& instance = Api::UnwrapInstanceHandle(zone, object);
|
|
ASSERT(!instance.IsNull());
|
|
return Api::NewHandle(thread, ByteBuffer::Data(instance));
|
|
}
|
|
|
|
// --- Invoking Constructors, Methods, and Field accessors ---
|
|
|
|
static ObjectPtr ResolveConstructor(const char* current_func,
|
|
const Class& cls,
|
|
const String& class_name,
|
|
const String& constr_name,
|
|
int num_args) {
|
|
// The constructor must be present in the interface.
|
|
Function& constructor = Function::Handle();
|
|
if (cls.EnsureIsFinalized(Thread::Current()) == Error::null()) {
|
|
constructor = cls.LookupFunctionAllowPrivate(constr_name);
|
|
}
|
|
if (constructor.IsNull() ||
|
|
(!constructor.IsGenerativeConstructor() && !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);
|
|
}
|
|
}
|
|
const int kTypeArgsLen = 0;
|
|
const int extra_args = 1;
|
|
String& error_message = String::Handle();
|
|
if (!constructor.AreValidArgumentCounts(kTypeArgsLen, 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);
|
|
}
|
|
ErrorPtr error = constructor.VerifyCallEntryPoint();
|
|
if (error != Error::null()) return error;
|
|
return constructor.ptr();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_New(Dart_Handle type,
|
|
Dart_Handle constructor_name,
|
|
int number_of_arguments,
|
|
Dart_Handle* arguments) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
Object& result = Object::Handle(Z);
|
|
|
|
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.
|
|
Object& unchecked_type = Object::Handle(Api::UnwrapHandle(type));
|
|
if (unchecked_type.IsNull() || !unchecked_type.IsType()) {
|
|
RETURN_TYPE_ERROR(Z, type, Type);
|
|
}
|
|
Type& type_obj = Type::Handle();
|
|
type_obj ^= unchecked_type.ptr();
|
|
if (!type_obj.IsFinalized()) {
|
|
return Api::NewError(
|
|
"%s expects argument 'type' to be a fully resolved type.",
|
|
CURRENT_FUNC);
|
|
}
|
|
Class& cls = Class::Handle(Z, type_obj.type_class());
|
|
CHECK_ERROR_HANDLE(cls.EnsureIsAllocateFinalized(T));
|
|
|
|
TypeArguments& type_arguments =
|
|
TypeArguments::Handle(Z, type_obj.arguments());
|
|
|
|
const String& base_constructor_name = String::Handle(Z, cls.Name());
|
|
|
|
// And get the name of the constructor to invoke.
|
|
String& dot_name = String::Handle(Z);
|
|
result = Api::UnwrapHandle(constructor_name);
|
|
if (result.IsNull()) {
|
|
dot_name = Symbols::Dot().ptr();
|
|
} else if (result.IsString()) {
|
|
dot_name = String::Concat(Symbols::Dot(), String::Cast(result));
|
|
} else {
|
|
RETURN_TYPE_ERROR(Z, constructor_name, String);
|
|
}
|
|
|
|
// Resolve the constructor.
|
|
String& constr_name =
|
|
String::Handle(String::Concat(base_constructor_name, dot_name));
|
|
result = ResolveConstructor("Dart_New", cls, base_constructor_name,
|
|
constr_name, number_of_arguments);
|
|
if (result.IsError()) {
|
|
return Api::NewHandle(T, result.ptr());
|
|
}
|
|
ASSERT(result.IsFunction());
|
|
Function& constructor = Function::Handle(Z);
|
|
constructor ^= result.ptr();
|
|
|
|
Instance& new_object = Instance::Handle(Z);
|
|
if (constructor.IsGenerativeConstructor()) {
|
|
CHECK_ERROR_HANDLE(cls.VerifyEntryPoint());
|
|
#if defined(DEBUG)
|
|
if (!cls.is_allocated() &&
|
|
(Dart::vm_snapshot_kind() == Snapshot::kFullAOT)) {
|
|
return Api::NewError("Precompilation dropped '%s'", cls.ToCString());
|
|
}
|
|
#endif
|
|
// Create the new object.
|
|
new_object = Instance::New(cls);
|
|
}
|
|
|
|
// Create the argument list.
|
|
intptr_t arg_index = 0;
|
|
int extra_args = 1;
|
|
const Array& args =
|
|
Array::Handle(Z, Array::New(number_of_arguments + extra_args));
|
|
if (constructor.IsGenerativeConstructor()) {
|
|
// Constructors get the uninitialized object.
|
|
if (!type_arguments.IsNull()) {
|
|
// The type arguments will be null if the class has no type parameters, in
|
|
// which case the following call would fail because there is no slot
|
|
// reserved in the object for the type vector.
|
|
new_object.SetTypeArguments(type_arguments);
|
|
}
|
|
args.SetAt(arg_index++, new_object);
|
|
} else {
|
|
// Factories get type arguments.
|
|
args.SetAt(arg_index++, type_arguments);
|
|
}
|
|
Object& argument = Object::Handle(Z);
|
|
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(T, argument.ptr());
|
|
} else {
|
|
return Api::NewError(
|
|
"%s expects arguments[%d] to be an Instance handle.", CURRENT_FUNC,
|
|
i);
|
|
}
|
|
}
|
|
args.SetAt(arg_index++, argument);
|
|
}
|
|
|
|
const int kTypeArgsLen = 0;
|
|
Array& args_descriptor_array = Array::Handle(
|
|
Z, ArgumentsDescriptor::NewBoxed(kTypeArgsLen, args.Length()));
|
|
|
|
ArgumentsDescriptor args_descriptor(args_descriptor_array);
|
|
ObjectPtr type_error = constructor.DoArgumentTypesMatch(
|
|
args, args_descriptor, type_arguments, Object::empty_type_arguments());
|
|
if (type_error != Error::null()) {
|
|
return Api::NewHandle(T, type_error);
|
|
}
|
|
|
|
// Invoke the constructor and return the new object.
|
|
result = DartEntry::InvokeFunction(constructor, args);
|
|
if (result.IsError()) {
|
|
return Api::NewHandle(T, result.ptr());
|
|
}
|
|
|
|
if (constructor.IsGenerativeConstructor()) {
|
|
ASSERT(result.IsNull());
|
|
} else {
|
|
ASSERT(result.IsNull() || result.IsInstance());
|
|
new_object ^= result.ptr();
|
|
}
|
|
return Api::NewHandle(T, new_object.ptr());
|
|
}
|
|
|
|
static InstancePtr AllocateObject(Thread* thread, const Class& cls) {
|
|
if (!cls.is_fields_marked_nullable()) {
|
|
// Mark all fields as nullable.
|
|
Zone* zone = thread->zone();
|
|
Class& iterate_cls = Class::Handle(zone, cls.ptr());
|
|
Field& field = Field::Handle(zone);
|
|
Array& fields = Array::Handle(zone);
|
|
SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
|
|
if (!cls.is_fields_marked_nullable()) {
|
|
while (!iterate_cls.IsNull()) {
|
|
ASSERT(iterate_cls.is_finalized());
|
|
iterate_cls.set_is_fields_marked_nullable();
|
|
fields = iterate_cls.fields();
|
|
iterate_cls = iterate_cls.SuperClass();
|
|
for (int field_num = 0; field_num < fields.Length(); field_num++) {
|
|
field ^= fields.At(field_num);
|
|
if (field.is_static()) {
|
|
continue;
|
|
}
|
|
field.RecordStore(Object::null_object());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Allocate an object for the given class.
|
|
return Instance::New(cls);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_Allocate(Dart_Handle type) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
const Type& type_obj = Api::UnwrapTypeHandle(Z, type);
|
|
// Get the class to instantiate.
|
|
if (type_obj.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, type, Type);
|
|
}
|
|
|
|
if (!type_obj.IsFinalized()) {
|
|
return Api::NewError(
|
|
"%s expects argument 'type' to be a fully resolved type.",
|
|
CURRENT_FUNC);
|
|
}
|
|
|
|
const Class& cls = Class::Handle(Z, type_obj.type_class());
|
|
const TypeArguments& type_arguments =
|
|
TypeArguments::Handle(Z, type_obj.arguments());
|
|
|
|
CHECK_ERROR_HANDLE(cls.VerifyEntryPoint());
|
|
#if defined(DEBUG)
|
|
if (!cls.is_allocated() && (Dart::vm_snapshot_kind() == Snapshot::kFullAOT)) {
|
|
return Api::NewError("Precompilation dropped '%s'", cls.ToCString());
|
|
}
|
|
#endif
|
|
CHECK_ERROR_HANDLE(cls.EnsureIsAllocateFinalized(T));
|
|
const Instance& new_obj = Instance::Handle(Z, AllocateObject(T, cls));
|
|
if (!type_arguments.IsNull()) {
|
|
new_obj.SetTypeArguments(type_arguments);
|
|
}
|
|
return Api::NewHandle(T, new_obj.ptr());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_AllocateWithNativeFields(Dart_Handle type,
|
|
intptr_t num_native_fields,
|
|
const intptr_t* native_fields) {
|
|
DARTSCOPE(Thread::Current());
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
const Type& type_obj = Api::UnwrapTypeHandle(Z, type);
|
|
// Get the class to instantiate.
|
|
if (type_obj.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, type, Type);
|
|
}
|
|
if (native_fields == NULL) {
|
|
RETURN_NULL_ERROR(native_fields);
|
|
}
|
|
const Class& cls = Class::Handle(Z, type_obj.type_class());
|
|
CHECK_ERROR_HANDLE(cls.VerifyEntryPoint());
|
|
#if defined(DEBUG)
|
|
if (!cls.is_allocated() && (Dart::vm_snapshot_kind() == Snapshot::kFullAOT)) {
|
|
return Api::NewError("Precompilation dropped '%s'", cls.ToCString());
|
|
}
|
|
#endif
|
|
CHECK_ERROR_HANDLE(cls.EnsureIsAllocateFinalized(T));
|
|
if (num_native_fields != cls.num_native_fields()) {
|
|
return Api::NewError(
|
|
"%s: invalid number of native fields %" Pd " passed in, expected %d",
|
|
CURRENT_FUNC, num_native_fields, cls.num_native_fields());
|
|
}
|
|
const Instance& instance = Instance::Handle(Z, AllocateObject(T, cls));
|
|
instance.SetNativeFields(num_native_fields, native_fields);
|
|
return Api::NewHandle(T, instance.ptr());
|
|
}
|
|
|
|
static Dart_Handle SetupArguments(Thread* thread,
|
|
int num_args,
|
|
Dart_Handle* arguments,
|
|
int extra_args,
|
|
Array* args) {
|
|
Zone* zone = thread->zone();
|
|
// Check for malformed arguments in the arguments list.
|
|
*args = Array::New(num_args + extra_args);
|
|
Object& arg = Object::Handle(zone);
|
|
for (int i = 0; i < num_args; i++) {
|
|
arg = Api::UnwrapHandle(arguments[i]);
|
|
if (!arg.IsNull() && !arg.IsInstance()) {
|
|
*args = Array::null();
|
|
if (arg.IsError()) {
|
|
return Api::NewHandle(thread, arg.ptr());
|
|
} else {
|
|
return Api::NewError(
|
|
"%s expects arguments[%d] to be an Instance handle.", "Dart_Invoke",
|
|
i);
|
|
}
|
|
}
|
|
args->SetAt((i + extra_args), arg);
|
|
}
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_InvokeConstructor(Dart_Handle object,
|
|
Dart_Handle name,
|
|
int number_of_arguments,
|
|
Dart_Handle* arguments) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
if (number_of_arguments < 0) {
|
|
return Api::NewError(
|
|
"%s expects argument 'number_of_arguments' to be non-negative.",
|
|
CURRENT_FUNC);
|
|
}
|
|
const Instance& instance = Api::UnwrapInstanceHandle(Z, object);
|
|
if (instance.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, object, Instance);
|
|
}
|
|
|
|
// Since we have allocated an object it would mean that the type
|
|
// is finalized.
|
|
// TODO(asiva): How do we ensure that a constructor is not called more than
|
|
// once for the same object.
|
|
|
|
// Construct name of the constructor to invoke.
|
|
const String& constructor_name = Api::UnwrapStringHandle(Z, name);
|
|
AbstractType& type_obj =
|
|
AbstractType::Handle(Z, instance.GetType(Heap::kNew));
|
|
const Class& cls = Class::Handle(Z, type_obj.type_class());
|
|
const String& class_name = String::Handle(Z, cls.Name());
|
|
const Array& strings = Array::Handle(Z, Array::New(3));
|
|
strings.SetAt(0, class_name);
|
|
strings.SetAt(1, Symbols::Dot());
|
|
if (constructor_name.IsNull()) {
|
|
strings.SetAt(2, Symbols::Empty());
|
|
} else {
|
|
strings.SetAt(2, constructor_name);
|
|
}
|
|
const String& dot_name = String::Handle(Z, String::ConcatAll(strings));
|
|
const TypeArguments& type_arguments =
|
|
TypeArguments::Handle(Z, type_obj.arguments());
|
|
const Function& constructor =
|
|
Function::Handle(Z, cls.LookupFunctionAllowPrivate(dot_name));
|
|
const int kTypeArgsLen = 0;
|
|
const int extra_args = 1;
|
|
if (!constructor.IsNull() && constructor.IsGenerativeConstructor() &&
|
|
constructor.AreValidArgumentCounts(
|
|
kTypeArgsLen, number_of_arguments + extra_args, 0, NULL)) {
|
|
CHECK_ERROR_HANDLE(constructor.VerifyCallEntryPoint());
|
|
// Create the argument list.
|
|
Dart_Handle result;
|
|
Array& args = Array::Handle(Z);
|
|
result =
|
|
SetupArguments(T, number_of_arguments, arguments, extra_args, &args);
|
|
if (!Api::IsError(result)) {
|
|
args.SetAt(0, instance);
|
|
|
|
const int kTypeArgsLen = 0;
|
|
const Array& args_descriptor_array = Array::Handle(
|
|
Z, ArgumentsDescriptor::NewBoxed(kTypeArgsLen, args.Length()));
|
|
ArgumentsDescriptor args_descriptor(args_descriptor_array);
|
|
ObjectPtr type_error = constructor.DoArgumentTypesMatch(
|
|
args, args_descriptor, type_arguments);
|
|
if (type_error != Error::null()) {
|
|
return Api::NewHandle(T, type_error);
|
|
}
|
|
|
|
const Object& retval =
|
|
Object::Handle(Z, DartEntry::InvokeFunction(constructor, args));
|
|
if (retval.IsError()) {
|
|
result = Api::NewHandle(T, retval.ptr());
|
|
} else {
|
|
result = Api::NewHandle(T, instance.ptr());
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
return Api::NewError("%s expects argument 'name' to be a valid constructor.",
|
|
CURRENT_FUNC);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target,
|
|
Dart_Handle name,
|
|
int number_of_arguments,
|
|
Dart_Handle* arguments) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
String& function_name =
|
|
String::Handle(Z, Api::UnwrapStringHandle(Z, name).ptr());
|
|
if (function_name.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, 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(Z, Api::UnwrapHandle(target));
|
|
if (obj.IsError()) {
|
|
return target;
|
|
}
|
|
Dart_Handle result;
|
|
Array& args = Array::Handle(Z);
|
|
// This API does not provide a way to pass named parameters.
|
|
const Array& arg_names = Object::empty_array();
|
|
const bool respect_reflectable = false;
|
|
const bool check_is_entrypoint = FLAG_verify_entry_points;
|
|
if (obj.IsType()) {
|
|
if (!Type::Cast(obj).IsFinalized()) {
|
|
return Api::NewError(
|
|
"%s expects argument 'target' to be a fully resolved type.",
|
|
CURRENT_FUNC);
|
|
}
|
|
|
|
const Class& cls = Class::Handle(Z, Type::Cast(obj).type_class());
|
|
if (Library::IsPrivate(function_name)) {
|
|
const Library& lib = Library::Handle(Z, cls.library());
|
|
function_name = lib.PrivateName(function_name);
|
|
}
|
|
|
|
// Setup args and check for malformed arguments in the arguments list.
|
|
result = SetupArguments(T, number_of_arguments, arguments, 0, &args);
|
|
if (Api::IsError(result)) {
|
|
return result;
|
|
}
|
|
return Api::NewHandle(
|
|
T, cls.Invoke(function_name, args, arg_names, respect_reflectable,
|
|
check_is_entrypoint));
|
|
} else if (obj.IsNull() || obj.IsInstance()) {
|
|
// Since we have allocated an object it would mean that the type of the
|
|
// receiver is already resolved and finalized, hence it is not necessary
|
|
// to check here.
|
|
Instance& instance = Instance::Handle(Z);
|
|
instance ^= obj.ptr();
|
|
|
|
// Setup args and check for malformed arguments in the arguments list.
|
|
result = SetupArguments(T, number_of_arguments, arguments, 1, &args);
|
|
if (Api::IsError(result)) {
|
|
return result;
|
|
}
|
|
args.SetAt(0, instance);
|
|
return Api::NewHandle(
|
|
T, instance.Invoke(function_name, args, arg_names, respect_reflectable,
|
|
check_is_entrypoint));
|
|
} else if (obj.IsLibrary()) {
|
|
// Check whether class finalization is needed.
|
|
const Library& lib = Library::Cast(obj);
|
|
|
|
// Check that the library is loaded.
|
|
if (!lib.Loaded()) {
|
|
return Api::NewError("%s expects library argument 'target' to be loaded.",
|
|
CURRENT_FUNC);
|
|
}
|
|
|
|
if (Library::IsPrivate(function_name)) {
|
|
function_name = lib.PrivateName(function_name);
|
|
}
|
|
|
|
// Setup args and check for malformed arguments in the arguments list.
|
|
result = SetupArguments(T, number_of_arguments, arguments, 0, &args);
|
|
if (Api::IsError(result)) {
|
|
return result;
|
|
}
|
|
|
|
return Api::NewHandle(
|
|
T, lib.Invoke(function_name, args, arg_names, respect_reflectable,
|
|
check_is_entrypoint));
|
|
} else {
|
|
return Api::NewError(
|
|
"%s expects argument 'target' to be an object, type, or library.",
|
|
CURRENT_FUNC);
|
|
}
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_InvokeClosure(Dart_Handle closure,
|
|
int number_of_arguments,
|
|
Dart_Handle* arguments) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
CHECK_CALLBACK_STATE(T);
|
|
const Instance& closure_obj = Api::UnwrapInstanceHandle(Z, closure);
|
|
if (closure_obj.IsNull() || !closure_obj.IsCallable(NULL)) {
|
|
RETURN_TYPE_ERROR(Z, closure, Instance);
|
|
}
|
|
if (number_of_arguments < 0) {
|
|
return Api::NewError(
|
|
"%s expects argument 'number_of_arguments' to be non-negative.",
|
|
CURRENT_FUNC);
|
|
}
|
|
|
|
// Set up arguments to include the closure as the first argument.
|
|
const Array& args = Array::Handle(Z, Array::New(number_of_arguments + 1));
|
|
Object& obj = Object::Handle(Z);
|
|
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(Z, arguments[i], Instance);
|
|
}
|
|
args.SetAt(i + 1, obj);
|
|
}
|
|
// Now try to invoke the closure.
|
|
return Api::NewHandle(T, DartEntry::InvokeClosure(T, args));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetField(Dart_Handle container, Dart_Handle name) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
String& field_name =
|
|
String::Handle(Z, Api::UnwrapStringHandle(Z, name).ptr());
|
|
if (field_name.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, name, String);
|
|
}
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(container));
|
|
const bool throw_nsm_if_absent = true;
|
|
const bool respect_reflectable = false;
|
|
const bool check_is_entrypoint = FLAG_verify_entry_points;
|
|
|
|
if (obj.IsType()) {
|
|
if (!Type::Cast(obj).IsFinalized()) {
|
|
return Api::NewError(
|
|
"%s expects argument 'container' to be a fully resolved type.",
|
|
CURRENT_FUNC);
|
|
}
|
|
Class& cls = Class::Handle(Z, Type::Cast(obj).type_class());
|
|
if (Library::IsPrivate(field_name)) {
|
|
const Library& lib = Library::Handle(Z, cls.library());
|
|
field_name = lib.PrivateName(field_name);
|
|
}
|
|
return Api::NewHandle(
|
|
T, cls.InvokeGetter(field_name, throw_nsm_if_absent,
|
|
respect_reflectable, check_is_entrypoint));
|
|
} else if (obj.IsNull() || obj.IsInstance()) {
|
|
Instance& instance = Instance::Handle(Z);
|
|
instance ^= obj.ptr();
|
|
if (Library::IsPrivate(field_name)) {
|
|
const Class& cls = Class::Handle(Z, instance.clazz());
|
|
const Library& lib = Library::Handle(Z, cls.library());
|
|
field_name = lib.PrivateName(field_name);
|
|
}
|
|
return Api::NewHandle(T,
|
|
instance.InvokeGetter(field_name, respect_reflectable,
|
|
check_is_entrypoint));
|
|
} else if (obj.IsLibrary()) {
|
|
const Library& lib = Library::Cast(obj);
|
|
// Check that the library is loaded.
|
|
if (!lib.Loaded()) {
|
|
return Api::NewError(
|
|
"%s expects library argument 'container' to be loaded.",
|
|
CURRENT_FUNC);
|
|
}
|
|
if (Library::IsPrivate(field_name)) {
|
|
field_name = lib.PrivateName(field_name);
|
|
}
|
|
return Api::NewHandle(
|
|
T, lib.InvokeGetter(field_name, throw_nsm_if_absent,
|
|
respect_reflectable, check_is_entrypoint));
|
|
} else if (obj.IsError()) {
|
|
return container;
|
|
} else {
|
|
return Api::NewError(
|
|
"%s expects argument 'container' to be an object, type, or library.",
|
|
CURRENT_FUNC);
|
|
}
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_SetField(Dart_Handle container,
|
|
Dart_Handle name,
|
|
Dart_Handle value) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
String& field_name =
|
|
String::Handle(Z, Api::UnwrapStringHandle(Z, name).ptr());
|
|
if (field_name.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, name, String);
|
|
}
|
|
|
|
// Since null is allowed for value, we don't use UnwrapInstanceHandle.
|
|
const Object& value_obj = Object::Handle(Z, Api::UnwrapHandle(value));
|
|
if (!value_obj.IsNull() && !value_obj.IsInstance()) {
|
|
RETURN_TYPE_ERROR(Z, value, Instance);
|
|
}
|
|
Instance& value_instance = Instance::Handle(Z);
|
|
value_instance ^= value_obj.ptr();
|
|
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(container));
|
|
const bool respect_reflectable = false;
|
|
const bool check_is_entrypoint = FLAG_verify_entry_points;
|
|
|
|
if (obj.IsType()) {
|
|
if (!Type::Cast(obj).IsFinalized()) {
|
|
return Api::NewError(
|
|
"%s expects argument 'container' to be a fully resolved type.",
|
|
CURRENT_FUNC);
|
|
}
|
|
|
|
// To access a static field we may need to use the Field or the
|
|
// setter Function.
|
|
Class& cls = Class::Handle(Z, Type::Cast(obj).type_class());
|
|
if (Library::IsPrivate(field_name)) {
|
|
const Library& lib = Library::Handle(Z, cls.library());
|
|
field_name = lib.PrivateName(field_name);
|
|
}
|
|
return Api::NewHandle(
|
|
T, cls.InvokeSetter(field_name, value_instance, respect_reflectable,
|
|
check_is_entrypoint));
|
|
} else if (obj.IsNull() || obj.IsInstance()) {
|
|
Instance& instance = Instance::Handle(Z);
|
|
instance ^= obj.ptr();
|
|
if (Library::IsPrivate(field_name)) {
|
|
const Class& cls = Class::Handle(Z, instance.clazz());
|
|
const Library& lib = Library::Handle(Z, cls.library());
|
|
field_name = lib.PrivateName(field_name);
|
|
}
|
|
return Api::NewHandle(
|
|
T, instance.InvokeSetter(field_name, value_instance,
|
|
respect_reflectable, check_is_entrypoint));
|
|
} 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);
|
|
// Check that the library is loaded.
|
|
if (!lib.Loaded()) {
|
|
return Api::NewError(
|
|
"%s expects library argument 'container' to be loaded.",
|
|
CURRENT_FUNC);
|
|
}
|
|
|
|
if (Library::IsPrivate(field_name)) {
|
|
field_name = lib.PrivateName(field_name);
|
|
}
|
|
return Api::NewHandle(
|
|
T, lib.InvokeSetter(field_name, value_instance, respect_reflectable,
|
|
check_is_entrypoint));
|
|
} else if (obj.IsError()) {
|
|
return container;
|
|
}
|
|
return Api::NewError(
|
|
"%s expects argument 'container' to be an object, type, or library.",
|
|
CURRENT_FUNC);
|
|
}
|
|
|
|
// --- Exceptions ----
|
|
|
|
DART_EXPORT Dart_Handle Dart_ThrowException(Dart_Handle exception) {
|
|
Thread* thread = Thread::Current();
|
|
Zone* zone = thread->zone();
|
|
Isolate* isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
CHECK_CALLBACK_STATE(thread);
|
|
if (::Dart_IsError(exception)) {
|
|
::Dart_PropagateError(exception);
|
|
}
|
|
TransitionNativeToVM transition(thread);
|
|
const Instance& excp = Api::UnwrapInstanceHandle(zone, exception);
|
|
if (excp.IsNull()) {
|
|
RETURN_TYPE_ERROR(zone, exception, Instance);
|
|
}
|
|
if (thread->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.
|
|
const Instance* saved_exception;
|
|
{
|
|
NoSafepointScope no_safepoint;
|
|
InstancePtr raw_exception =
|
|
Api::UnwrapInstanceHandle(zone, exception).ptr();
|
|
thread->UnwindScopes(thread->top_exit_frame_info());
|
|
saved_exception = &Instance::Handle(raw_exception);
|
|
}
|
|
Exceptions::Throw(thread, *saved_exception);
|
|
return Api::NewError("Exception was not thrown, internal error");
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_ReThrowException(Dart_Handle exception,
|
|
Dart_Handle stacktrace) {
|
|
Thread* thread = Thread::Current();
|
|
Zone* zone = thread->zone();
|
|
Isolate* isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
CHECK_CALLBACK_STATE(thread);
|
|
TransitionNativeToVM transition(thread);
|
|
{
|
|
const Instance& excp = Api::UnwrapInstanceHandle(zone, exception);
|
|
if (excp.IsNull()) {
|
|
RETURN_TYPE_ERROR(zone, exception, Instance);
|
|
}
|
|
const Instance& stk = Api::UnwrapInstanceHandle(zone, stacktrace);
|
|
if (stk.IsNull()) {
|
|
RETURN_TYPE_ERROR(zone, stacktrace, Instance);
|
|
}
|
|
}
|
|
if (thread->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.
|
|
const Instance* saved_exception;
|
|
const StackTrace* saved_stacktrace;
|
|
{
|
|
NoSafepointScope no_safepoint;
|
|
InstancePtr raw_exception =
|
|
Api::UnwrapInstanceHandle(zone, exception).ptr();
|
|
StackTracePtr raw_stacktrace =
|
|
Api::UnwrapStackTraceHandle(zone, stacktrace).ptr();
|
|
thread->UnwindScopes(thread->top_exit_frame_info());
|
|
saved_exception = &Instance::Handle(raw_exception);
|
|
saved_stacktrace = &StackTrace::Handle(raw_stacktrace);
|
|
}
|
|
Exceptions::ReThrow(thread, *saved_exception, *saved_stacktrace);
|
|
return Api::NewError("Exception was not re thrown, internal error");
|
|
}
|
|
|
|
// --- Native fields and functions ---
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetNativeInstanceFieldCount(Dart_Handle obj,
|
|
int* count) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
{
|
|
ReusableObjectHandleScope reused_obj_handle(thread);
|
|
const Instance& instance =
|
|
Api::UnwrapInstanceHandle(reused_obj_handle, obj);
|
|
if (!instance.IsNull()) {
|
|
*count = instance.NumNativeFields();
|
|
return Api::Success();
|
|
}
|
|
}
|
|
RETURN_TYPE_ERROR(thread->zone(), obj, Instance);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetNativeInstanceField(Dart_Handle obj,
|
|
int index,
|
|
intptr_t* value) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
bool is_null = false;
|
|
{
|
|
ReusableObjectHandleScope reused_obj_handle(thread);
|
|
const Instance& instance =
|
|
Api::UnwrapInstanceHandle(reused_obj_handle, obj);
|
|
if (!instance.IsNull()) {
|
|
if (instance.IsValidNativeIndex(index)) {
|
|
*value = instance.GetNativeField(index);
|
|
return Api::Success();
|
|
}
|
|
} else {
|
|
is_null = true;
|
|
}
|
|
}
|
|
if (is_null) {
|
|
RETURN_TYPE_ERROR(thread->zone(), obj, Instance);
|
|
}
|
|
return Api::NewError(
|
|
"%s: invalid index %d passed in to access native instance field",
|
|
CURRENT_FUNC, index);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_SetNativeInstanceField(Dart_Handle obj,
|
|
int index,
|
|
intptr_t value) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Instance& instance = Api::UnwrapInstanceHandle(Z, obj);
|
|
if (instance.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, 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();
|
|
}
|
|
|
|
DART_EXPORT void* Dart_GetNativeIsolateGroupData(Dart_NativeArguments args) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
|
Isolate* isolate = arguments->thread()->isolate();
|
|
ASSERT(isolate == Isolate::Current());
|
|
return isolate->init_callback_data();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetNativeArguments(
|
|
Dart_NativeArguments args,
|
|
int num_arguments,
|
|
const Dart_NativeArgument_Descriptor* argument_descriptors,
|
|
Dart_NativeArgument_Value* arg_values) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
|
TransitionNativeToVM transition(arguments->thread());
|
|
ASSERT(arguments->thread()->isolate() == Isolate::Current());
|
|
if (arg_values == NULL) {
|
|
RETURN_NULL_ERROR(arg_values);
|
|
}
|
|
for (int i = 0; i < num_arguments; i++) {
|
|
Dart_NativeArgument_Descriptor desc = argument_descriptors[i];
|
|
Dart_NativeArgument_Type arg_type =
|
|
static_cast<Dart_NativeArgument_Type>(desc.type);
|
|
int arg_index = desc.index;
|
|
ASSERT(arg_index >= 0 && arg_index < arguments->NativeArgCount());
|
|
Dart_NativeArgument_Value* native_value = &(arg_values[i]);
|
|
switch (arg_type) {
|
|
case Dart_NativeArgument_kBool:
|
|
if (!Api::GetNativeBooleanArgument(arguments, arg_index,
|
|
&(native_value->as_bool))) {
|
|
return Api::NewArgumentError(
|
|
"%s: expects argument at index %d to be of"
|
|
" type Boolean.",
|
|
CURRENT_FUNC, i);
|
|
}
|
|
break;
|
|
|
|
case Dart_NativeArgument_kInt32: {
|
|
int64_t value = 0;
|
|
if (!GetNativeIntegerArgument(arguments, arg_index, &value)) {
|
|
return Api::NewArgumentError(
|
|
"%s: expects argument at index %d to be of"
|
|
" type Integer.",
|
|
CURRENT_FUNC, i);
|
|
}
|
|
if (value < INT32_MIN || value > INT32_MAX) {
|
|
return Api::NewArgumentError(
|
|
"%s: argument value at index %d is out of range", CURRENT_FUNC,
|
|
i);
|
|
}
|
|
native_value->as_int32 = static_cast<int32_t>(value);
|
|
break;
|
|
}
|
|
|
|
case Dart_NativeArgument_kUint32: {
|
|
int64_t value = 0;
|
|
if (!GetNativeIntegerArgument(arguments, arg_index, &value)) {
|
|
return Api::NewArgumentError(
|
|
"%s: expects argument at index %d to be of"
|
|
" type Integer.",
|
|
CURRENT_FUNC, i);
|
|
}
|
|
if (value < 0 || value > UINT32_MAX) {
|
|
return Api::NewArgumentError(
|
|
"%s: argument value at index %d is out of range", CURRENT_FUNC,
|
|
i);
|
|
}
|
|
native_value->as_uint32 = static_cast<uint32_t>(value);
|
|
break;
|
|
}
|
|
|
|
case Dart_NativeArgument_kInt64: {
|
|
int64_t value = 0;
|
|
if (!GetNativeIntegerArgument(arguments, arg_index, &value)) {
|
|
return Api::NewArgumentError(
|
|
"%s: expects argument at index %d to be of"
|
|
" type Integer.",
|
|
CURRENT_FUNC, i);
|
|
}
|
|
native_value->as_int64 = value;
|
|
break;
|
|
}
|
|
|
|
case Dart_NativeArgument_kUint64: {
|
|
uint64_t value = 0;
|
|
if (!GetNativeUnsignedIntegerArgument(arguments, arg_index, &value)) {
|
|
return Api::NewArgumentError(
|
|
"%s: expects argument at index %d to be of"
|
|
" type Integer.",
|
|
CURRENT_FUNC, i);
|
|
}
|
|
native_value->as_uint64 = value;
|
|
break;
|
|
}
|
|
|
|
case Dart_NativeArgument_kDouble:
|
|
if (!GetNativeDoubleArgument(arguments, arg_index,
|
|
&(native_value->as_double))) {
|
|
return Api::NewArgumentError(
|
|
"%s: expects argument at index %d to be of"
|
|
" type Double.",
|
|
CURRENT_FUNC, i);
|
|
}
|
|
break;
|
|
|
|
case Dart_NativeArgument_kString:
|
|
if (!GetNativeStringArgument(arguments, arg_index,
|
|
&(native_value->as_string.dart_str),
|
|
&(native_value->as_string.peer))) {
|
|
return Api::NewArgumentError(
|
|
"%s: expects argument at index %d to be of"
|
|
" type String.",
|
|
CURRENT_FUNC, i);
|
|
}
|
|
break;
|
|
|
|
case Dart_NativeArgument_kNativeFields: {
|
|
Dart_Handle result = GetNativeFieldsOfArgument(
|
|
arguments, arg_index, native_value->as_native_fields.num_fields,
|
|
native_value->as_native_fields.values, CURRENT_FUNC);
|
|
if (result != Api::Success()) {
|
|
return result;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Dart_NativeArgument_kInstance: {
|
|
ASSERT(arguments->thread() == Thread::Current());
|
|
ASSERT(arguments->thread()->api_top_scope() != NULL);
|
|
native_value->as_instance = Api::NewHandle(
|
|
arguments->thread(), arguments->NativeArgAt(arg_index));
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return Api::NewArgumentError("%s: invalid argument type %d.",
|
|
CURRENT_FUNC, arg_type);
|
|
}
|
|
}
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetNativeArgument(Dart_NativeArguments args,
|
|
int index) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(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);
|
|
}
|
|
TransitionNativeToVM transition(arguments->thread());
|
|
return Api::NewHandle(arguments->thread(), arguments->NativeArgAt(index));
|
|
}
|
|
|
|
DART_EXPORT int Dart_GetNativeArgumentCount(Dart_NativeArguments args) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
|
return arguments->NativeArgCount();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_GetNativeFieldsOfArgument(Dart_NativeArguments args,
|
|
int arg_index,
|
|
int num_fields,
|
|
intptr_t* field_values) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
|
if ((arg_index < 0) || (arg_index >= arguments->NativeArgCount())) {
|
|
return Api::NewError(
|
|
"%s: argument 'arg_index' out of range. Expected 0..%d but saw %d.",
|
|
CURRENT_FUNC, arguments->NativeArgCount() - 1, arg_index);
|
|
}
|
|
if (field_values == NULL) {
|
|
RETURN_NULL_ERROR(field_values);
|
|
}
|
|
return GetNativeFieldsOfArgument(arguments, arg_index, num_fields,
|
|
field_values, CURRENT_FUNC);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetNativeReceiver(Dart_NativeArguments args,
|
|
intptr_t* value) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
|
TransitionNativeToVM transition(arguments->thread());
|
|
ASSERT(arguments->thread()->isolate() == Isolate::Current());
|
|
if (value == NULL) {
|
|
RETURN_NULL_ERROR(value);
|
|
}
|
|
if (Api::GetNativeReceiver(arguments, value)) {
|
|
return Api::Success();
|
|
}
|
|
return Api::NewError(
|
|
"%s expects receiver argument to be non-null and of"
|
|
" type Instance.",
|
|
CURRENT_FUNC);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetNativeStringArgument(Dart_NativeArguments args,
|
|
int arg_index,
|
|
void** peer) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
|
TransitionNativeToVM transition(arguments->thread());
|
|
Dart_Handle result = Api::Null();
|
|
if (!GetNativeStringArgument(arguments, arg_index, &result, peer)) {
|
|
return Api::NewArgumentError(
|
|
"%s expects argument at %d to be of"
|
|
" type String.",
|
|
CURRENT_FUNC, arg_index);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetNativeIntegerArgument(Dart_NativeArguments args,
|
|
int index,
|
|
int64_t* value) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(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);
|
|
}
|
|
if (!GetNativeIntegerArgument(arguments, index, value)) {
|
|
return Api::NewArgumentError(
|
|
"%s: expects argument at %d to be of"
|
|
" type Integer.",
|
|
CURRENT_FUNC, index);
|
|
}
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetNativeBooleanArgument(Dart_NativeArguments args,
|
|
int index,
|
|
bool* value) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(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);
|
|
}
|
|
if (!Api::GetNativeBooleanArgument(arguments, index, value)) {
|
|
return Api::NewArgumentError(
|
|
"%s: expects argument at %d to be of type Boolean.", CURRENT_FUNC,
|
|
index);
|
|
}
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetNativeDoubleArgument(Dart_NativeArguments args,
|
|
int index,
|
|
double* value) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(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);
|
|
}
|
|
if (!GetNativeDoubleArgument(arguments, index, value)) {
|
|
return Api::NewArgumentError(
|
|
"%s: expects argument at %d to be of"
|
|
" type Double.",
|
|
CURRENT_FUNC, index);
|
|
}
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT void Dart_SetReturnValue(Dart_NativeArguments args,
|
|
Dart_Handle retval) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
|
ASSERT(arguments->thread()->isolate() == Isolate::Current());
|
|
ASSERT_CALLBACK_STATE(arguments->thread());
|
|
TransitionNativeToVM transition(arguments->thread());
|
|
if ((retval != Api::Null()) && !Api::IsInstance(retval) &&
|
|
!Api::IsError(retval)) {
|
|
// Print the current stack trace to make the problematic caller
|
|
// easier to find.
|
|
const StackTrace& stacktrace = GetCurrentStackTrace(0);
|
|
OS::PrintErr("=== Current Trace:\n%s===\n", stacktrace.ToCString());
|
|
|
|
const Object& ret_obj = Object::Handle(Api::UnwrapHandle(retval));
|
|
FATAL1(
|
|
"Return value check failed: saw '%s' expected a dart Instance or "
|
|
"an Error.",
|
|
ret_obj.ToCString());
|
|
}
|
|
ASSERT(retval != 0);
|
|
Api::SetReturnValue(arguments, retval);
|
|
}
|
|
|
|
DART_EXPORT void Dart_SetWeakHandleReturnValue(Dart_NativeArguments args,
|
|
Dart_WeakPersistentHandle rval) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
|
TransitionNativeToVM transition(arguments->thread());
|
|
#if defined(DEBUG)
|
|
Isolate* isolate = arguments->thread()->isolate();
|
|
ASSERT(isolate == Isolate::Current());
|
|
ASSERT(isolate->group()->api_state() != NULL &&
|
|
(isolate->group()->api_state()->IsValidWeakPersistentHandle(rval)));
|
|
#endif
|
|
Api::SetWeakHandleReturnValue(arguments, rval);
|
|
}
|
|
|
|
// --- Environment ---
|
|
StringPtr Api::GetEnvironmentValue(Thread* thread, const String& name) {
|
|
String& result = String::Handle(CallEnvironmentCallback(thread, name));
|
|
if (result.IsNull()) {
|
|
// Every 'dart:X' library introduces an environment variable
|
|
// 'dart.library.X' that is set to 'true'.
|
|
// We just need to make sure to hide private libraries (starting with
|
|
// "_", and the mirrors library, if it is not supported.
|
|
|
|
if (!FLAG_enable_mirrors && name.Equals(Symbols::DartLibraryMirrors())) {
|
|
return Symbols::False().ptr();
|
|
}
|
|
|
|
if (!Api::IsFfiEnabled() && name.Equals(Symbols::DartLibraryFfi())) {
|
|
return Symbols::False().ptr();
|
|
}
|
|
|
|
if (name.Equals(Symbols::DartVMProduct())) {
|
|
#ifdef PRODUCT
|
|
return Symbols::True().ptr();
|
|
#else
|
|
return Symbols::False().ptr();
|
|
#endif
|
|
}
|
|
|
|
if (name.Equals(Symbols::DartDeveloperTimeline())) {
|
|
#ifdef SUPPORT_TIMELINE
|
|
return Symbols::True().ptr();
|
|
#else
|
|
return Symbols::False().ptr();
|
|
#endif
|
|
}
|
|
|
|
const String& prefix = Symbols::DartLibrary();
|
|
if (name.StartsWith(prefix)) {
|
|
const String& library_name =
|
|
String::Handle(String::SubString(name, prefix.Length()));
|
|
|
|
// Private libraries (starting with "_") are not exposed to the user.
|
|
if (!library_name.IsNull() && library_name.CharAt(0) != '_') {
|
|
const String& dart_library_name =
|
|
String::Handle(String::Concat(Symbols::DartScheme(), library_name));
|
|
const Library& library =
|
|
Library::Handle(Library::LookupLibrary(thread, dart_library_name));
|
|
if (!library.IsNull()) {
|
|
return Symbols::True().ptr();
|
|
}
|
|
}
|
|
}
|
|
// Check for default VM provided values. If it was not overridden on the
|
|
// command line.
|
|
if (Symbols::DartIsVM().Equals(name)) {
|
|
return Symbols::True().ptr();
|
|
}
|
|
}
|
|
return result.ptr();
|
|
}
|
|
|
|
StringPtr Api::CallEnvironmentCallback(Thread* thread, const String& name) {
|
|
Isolate* isolate = thread->isolate();
|
|
Dart_EnvironmentCallback callback = isolate->environment_callback();
|
|
if (callback != NULL) {
|
|
Scope api_scope(thread);
|
|
Dart_Handle api_name = Api::NewHandle(thread, name.ptr());
|
|
Dart_Handle api_response;
|
|
{
|
|
TransitionVMToNative transition(thread);
|
|
api_response = callback(api_name);
|
|
}
|
|
const Object& response =
|
|
Object::Handle(thread->zone(), Api::UnwrapHandle(api_response));
|
|
if (response.IsString()) {
|
|
return String::Cast(response).ptr();
|
|
} else if (response.IsError()) {
|
|
Exceptions::ThrowArgumentError(
|
|
String::Handle(String::New(Error::Cast(response).ToErrorCString())));
|
|
} else if (!response.IsNull()) {
|
|
// At this point everything except null are invalid environment values.
|
|
Exceptions::ThrowArgumentError(
|
|
String::Handle(String::New("Illegal environment value")));
|
|
}
|
|
}
|
|
return String::null();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_SetEnvironmentCallback(Dart_EnvironmentCallback callback) {
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
isolate->set_environment_callback(callback);
|
|
return Api::Success();
|
|
}
|
|
|
|
// --- Scripts and Libraries ---
|
|
DART_EXPORT void Dart_SetBooleanReturnValue(Dart_NativeArguments args,
|
|
bool retval) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
|
TransitionNativeToVM transition(arguments->thread());
|
|
ASSERT(arguments->thread()->isolate() == Isolate::Current());
|
|
ASSERT_CALLBACK_STATE(arguments->thread());
|
|
arguments->SetReturn(Bool::Get(retval));
|
|
}
|
|
|
|
DART_EXPORT void Dart_SetIntegerReturnValue(Dart_NativeArguments args,
|
|
int64_t retval) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
|
TransitionNativeToVM transition(arguments->thread());
|
|
ASSERT(arguments->thread()->isolate() == Isolate::Current());
|
|
ASSERT_CALLBACK_STATE(arguments->thread());
|
|
if (Smi::IsValid(retval)) {
|
|
Api::SetSmiReturnValue(arguments, static_cast<intptr_t>(retval));
|
|
} else {
|
|
// Slow path for Mints.
|
|
Api::SetIntegerReturnValue(arguments, retval);
|
|
}
|
|
}
|
|
|
|
DART_EXPORT void Dart_SetDoubleReturnValue(Dart_NativeArguments args,
|
|
double retval) {
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
|
ASSERT(arguments->thread()->isolate() == Isolate::Current());
|
|
ASSERT_CALLBACK_STATE(arguments->thread());
|
|
TransitionNativeToVM transition(arguments->thread());
|
|
Api::SetDoubleReturnValue(arguments, retval);
|
|
}
|
|
|
|
// --- Scripts and Libraries ---
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_SetLibraryTagHandler(Dart_LibraryTagHandler handler) {
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
isolate->group()->set_library_tag_handler(handler);
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_DefaultCanonicalizeUrl(Dart_Handle base_url,
|
|
Dart_Handle url) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
const String& base_uri = Api::UnwrapStringHandle(Z, base_url);
|
|
if (base_uri.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, base_url, String);
|
|
}
|
|
const String& uri = Api::UnwrapStringHandle(Z, url);
|
|
if (uri.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, url, String);
|
|
}
|
|
|
|
const char* resolved_uri;
|
|
if (!ResolveUri(uri.ToCString(), base_uri.ToCString(), &resolved_uri)) {
|
|
return Api::NewError("%s: Unable to canonicalize uri '%s'.", CURRENT_FUNC,
|
|
uri.ToCString());
|
|
}
|
|
return Api::NewHandle(T, String::New(resolved_uri));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_SetDeferredLoadHandler(Dart_DeferredLoadHandler handler) {
|
|
Isolate* isolate = Isolate::Current();
|
|
CHECK_ISOLATE(isolate);
|
|
isolate->group()->set_deferred_load_handler(handler);
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_LoadScriptFromKernel(const uint8_t* buffer,
|
|
intptr_t buffer_size) {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
return Api::NewError("%s: Cannot compile on an AOT runtime.", CURRENT_FUNC);
|
|
#else
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
StackZone zone(T);
|
|
IsolateGroup* IG = T->isolate_group();
|
|
|
|
Library& library = Library::Handle(Z, IG->object_store()->root_library());
|
|
if (!library.IsNull()) {
|
|
const String& library_url = String::Handle(Z, library.url());
|
|
return Api::NewError("%s: A script has already been loaded from '%s'.",
|
|
CURRENT_FUNC, library_url.ToCString());
|
|
}
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
// NOTE: We do not attach a finalizer for this object, because the embedder
|
|
// will free it once the isolate group has shutdown.
|
|
const auto& td = ExternalTypedData::Handle(ExternalTypedData::New(
|
|
kExternalTypedDataUint8ArrayCid, const_cast<uint8_t*>(buffer),
|
|
buffer_size, Heap::kOld));
|
|
|
|
const char* error = nullptr;
|
|
std::unique_ptr<kernel::Program> program =
|
|
kernel::Program::ReadFromTypedData(td, &error);
|
|
if (program == nullptr) {
|
|
return Api::NewError("Can't load Kernel binary: %s.", error);
|
|
}
|
|
const Object& tmp = kernel::KernelLoader::LoadEntireProgram(program.get());
|
|
program.reset();
|
|
|
|
if (tmp.IsError()) {
|
|
return Api::NewHandle(T, tmp.ptr());
|
|
}
|
|
|
|
IG->source()->script_kernel_size = buffer_size;
|
|
IG->source()->script_kernel_buffer = buffer;
|
|
|
|
// TODO(32618): Setting root library based on whether it has 'main' or not
|
|
// is not correct because main can be in the exported namespace of a library
|
|
// or it could be a getter.
|
|
if (tmp.IsNull()) {
|
|
return Api::NewError(
|
|
"Invoked Dart programs must have a 'main' function defined:\n"
|
|
"https://dart.dev/guides/language/"
|
|
"language-tour#a-basic-dart-program");
|
|
}
|
|
library ^= tmp.ptr();
|
|
IG->object_store()->set_root_library(library);
|
|
return Api::NewHandle(T, library.ptr());
|
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_RootLibrary() {
|
|
Thread* thread = Thread::Current();
|
|
Isolate* isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
TransitionNativeToVM transition(thread);
|
|
return Api::NewHandle(thread,
|
|
isolate->group()->object_store()->root_library());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_SetRootLibrary(Dart_Handle library) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(library));
|
|
if (obj.IsNull() || obj.IsLibrary()) {
|
|
Library& lib = Library::Handle(Z);
|
|
lib ^= obj.ptr();
|
|
T->isolate_group()->object_store()->set_root_library(lib);
|
|
return library;
|
|
}
|
|
RETURN_TYPE_ERROR(Z, library, Library);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetClass(Dart_Handle library,
|
|
Dart_Handle class_name) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Library& lib = Api::UnwrapLibraryHandle(Z, library);
|
|
if (lib.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, library, Library);
|
|
}
|
|
const String& cls_name = Api::UnwrapStringHandle(Z, class_name);
|
|
if (cls_name.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, class_name, String);
|
|
}
|
|
const Class& cls = Class::Handle(Z, lib.LookupClassAllowPrivate(cls_name));
|
|
if (cls.IsNull()) {
|
|
// TODO(turnidge): Return null or error in this case?
|
|
const String& lib_name = String::Handle(Z, lib.name());
|
|
return Api::NewError("Class '%s' not found in library '%s'.",
|
|
cls_name.ToCString(), lib_name.ToCString());
|
|
}
|
|
cls.EnsureDeclarationLoaded();
|
|
CHECK_ERROR_HANDLE(cls.VerifyEntryPoint());
|
|
return Api::NewHandle(T, cls.RareType());
|
|
}
|
|
|
|
static Dart_Handle GetTypeCommon(Dart_Handle library,
|
|
Dart_Handle class_name,
|
|
intptr_t number_of_type_arguments,
|
|
Dart_Handle* type_arguments,
|
|
Nullability nullability) {
|
|
DARTSCOPE(Thread::Current());
|
|
// Validate the input arguments.
|
|
const Library& lib = Api::UnwrapLibraryHandle(Z, library);
|
|
if (lib.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, library, Library);
|
|
}
|
|
if (!lib.Loaded()) {
|
|
return Api::NewError("%s expects library argument 'library' to be loaded.",
|
|
CURRENT_FUNC);
|
|
}
|
|
const String& name_str = Api::UnwrapStringHandle(Z, class_name);
|
|
if (name_str.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, class_name, String);
|
|
}
|
|
const Class& cls = Class::Handle(Z, lib.LookupClassAllowPrivate(name_str));
|
|
if (cls.IsNull()) {
|
|
const String& lib_name = String::Handle(Z, lib.name());
|
|
return Api::NewError("Type '%s' not found in library '%s'.",
|
|
name_str.ToCString(), lib_name.ToCString());
|
|
}
|
|
cls.EnsureDeclarationLoaded();
|
|
CHECK_ERROR_HANDLE(cls.VerifyEntryPoint());
|
|
|
|
Type& type = Type::Handle();
|
|
if (cls.NumTypeArguments() == 0) {
|
|
if (number_of_type_arguments != 0) {
|
|
return Api::NewError(
|
|
"Invalid number of type arguments specified, "
|
|
"got %" Pd " expected 0",
|
|
number_of_type_arguments);
|
|
}
|
|
type ^= Type::NewNonParameterizedType(cls);
|
|
type ^= type.ToNullability(nullability, Heap::kOld);
|
|
} else {
|
|
intptr_t num_expected_type_arguments = cls.NumTypeParameters();
|
|
TypeArguments& type_args_obj = TypeArguments::Handle();
|
|
if (number_of_type_arguments > 0) {
|
|
if (type_arguments == NULL) {
|
|
RETURN_NULL_ERROR(type_arguments);
|
|
}
|
|
if (num_expected_type_arguments != number_of_type_arguments) {
|
|
return Api::NewError(
|
|
"Invalid number of type arguments specified, "
|
|
"got %" Pd " expected %" Pd,
|
|
number_of_type_arguments, num_expected_type_arguments);
|
|
}
|
|
const Array& array = Api::UnwrapArrayHandle(Z, *type_arguments);
|
|
if (array.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, *type_arguments, Array);
|
|
}
|
|
if (array.Length() != num_expected_type_arguments) {
|
|
return Api::NewError(
|
|
"Invalid type arguments specified, expected an "
|
|
"array of len %" Pd " but got an array of len %" Pd,
|
|
number_of_type_arguments, array.Length());
|
|
}
|
|
// Set up the type arguments array.
|
|
type_args_obj = TypeArguments::New(num_expected_type_arguments);
|
|
AbstractType& type_arg = AbstractType::Handle();
|
|
for (intptr_t i = 0; i < number_of_type_arguments; i++) {
|
|
type_arg ^= array.At(i);
|
|
type_args_obj.SetTypeAt(i, type_arg);
|
|
}
|
|
}
|
|
|
|
// Construct the type object, canonicalize it and return.
|
|
type ^= Type::New(cls, type_args_obj, nullability);
|
|
}
|
|
type ^= ClassFinalizer::FinalizeType(type);
|
|
return Api::NewHandle(T, type.ptr());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetType(Dart_Handle library,
|
|
Dart_Handle class_name,
|
|
intptr_t number_of_type_arguments,
|
|
Dart_Handle* type_arguments) {
|
|
if (IsolateGroup::Current()->null_safety()) {
|
|
return Api::NewError(
|
|
"Cannot use legacy types with --sound-null-safety enabled. "
|
|
"Use Dart_GetNullableType or Dart_GetNonNullableType instead.");
|
|
}
|
|
return GetTypeCommon(library, class_name, number_of_type_arguments,
|
|
type_arguments, Nullability::kLegacy);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetNullableType(Dart_Handle library,
|
|
Dart_Handle class_name,
|
|
intptr_t number_of_type_arguments,
|
|
Dart_Handle* type_arguments) {
|
|
return GetTypeCommon(library, class_name, number_of_type_arguments,
|
|
type_arguments, Nullability::kNullable);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_GetNonNullableType(Dart_Handle library,
|
|
Dart_Handle class_name,
|
|
intptr_t number_of_type_arguments,
|
|
Dart_Handle* type_arguments) {
|
|
return GetTypeCommon(library, class_name, number_of_type_arguments,
|
|
type_arguments, Nullability::kNonNullable);
|
|
}
|
|
|
|
static Dart_Handle TypeToHelper(Dart_Handle type, Nullability nullability) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Type& ty = Api::UnwrapTypeHandle(Z, type);
|
|
if (ty.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, type, Type);
|
|
}
|
|
if (ty.nullability() == nullability) {
|
|
return type;
|
|
}
|
|
return Api::NewHandle(T, ty.ToNullability(nullability, Heap::kOld));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_TypeToNullableType(Dart_Handle type) {
|
|
return TypeToHelper(type, Nullability::kNullable);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_TypeToNonNullableType(Dart_Handle type) {
|
|
return TypeToHelper(type, Nullability::kNonNullable);
|
|
}
|
|
|
|
static Dart_Handle IsOfTypeNullabilityHelper(Dart_Handle type,
|
|
Nullability nullability,
|
|
bool* result) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Type& ty = Api::UnwrapTypeHandle(Z, type);
|
|
if (ty.IsNull()) {
|
|
*result = false;
|
|
RETURN_TYPE_ERROR(Z, type, Type);
|
|
}
|
|
*result = (ty.nullability() == nullability);
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_IsNullableType(Dart_Handle type, bool* result) {
|
|
return IsOfTypeNullabilityHelper(type, Nullability::kNullable, result);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_IsNonNullableType(Dart_Handle type, bool* result) {
|
|
return IsOfTypeNullabilityHelper(type, Nullability::kNonNullable, result);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_IsLegacyType(Dart_Handle type, bool* result) {
|
|
return IsOfTypeNullabilityHelper(type, Nullability::kLegacy, result);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_LibraryUrl(Dart_Handle library) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Library& lib = Api::UnwrapLibraryHandle(Z, library);
|
|
if (lib.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, library, Library);
|
|
}
|
|
const String& url = String::Handle(Z, lib.url());
|
|
ASSERT(!url.IsNull());
|
|
return Api::NewHandle(T, url.ptr());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_LibraryResolvedUrl(Dart_Handle library) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Library& lib = Api::UnwrapLibraryHandle(Z, library);
|
|
if (lib.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, library, Library);
|
|
}
|
|
const Class& toplevel = Class::Handle(lib.toplevel_class());
|
|
ASSERT(!toplevel.IsNull());
|
|
const Script& script = Script::Handle(toplevel.script());
|
|
ASSERT(!script.IsNull());
|
|
const String& url = String::Handle(script.resolved_url());
|
|
ASSERT(!url.IsNull());
|
|
return Api::NewHandle(T, url.ptr());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetLoadedLibraries() {
|
|
DARTSCOPE(Thread::Current());
|
|
auto IG = T->isolate_group();
|
|
|
|
const GrowableObjectArray& libs =
|
|
GrowableObjectArray::Handle(Z, IG->object_store()->libraries());
|
|
int num_libs = libs.Length();
|
|
|
|
// Create new list and populate with the loaded libraries.
|
|
Library& lib = Library::Handle();
|
|
const Array& library_list = Array::Handle(Z, Array::New(num_libs));
|
|
for (int i = 0; i < num_libs; i++) {
|
|
lib ^= libs.At(i);
|
|
ASSERT(!lib.IsNull());
|
|
library_list.SetAt(i, lib);
|
|
}
|
|
return Api::NewHandle(T, library_list.ptr());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_LookupLibrary(Dart_Handle url) {
|
|
DARTSCOPE(Thread::Current());
|
|
const String& url_str = Api::UnwrapStringHandle(Z, url);
|
|
if (url_str.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, url, String);
|
|
}
|
|
const Library& library =
|
|
Library::Handle(Z, Library::LookupLibrary(T, url_str));
|
|
if (library.IsNull()) {
|
|
return Api::NewError("%s: library '%s' not found.", CURRENT_FUNC,
|
|
url_str.ToCString());
|
|
} else {
|
|
return Api::NewHandle(T, library.ptr());
|
|
}
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_LibraryHandleError(Dart_Handle library_in,
|
|
Dart_Handle error_in) {
|
|
DARTSCOPE(Thread::Current());
|
|
|
|
const Library& lib = Api::UnwrapLibraryHandle(Z, library_in);
|
|
if (lib.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, library_in, Library);
|
|
}
|
|
const Instance& err = Api::UnwrapInstanceHandle(Z, error_in);
|
|
if (err.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, error_in, Instance);
|
|
}
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
return error_in;
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_LoadLibraryFromKernel(const uint8_t* buffer,
|
|
intptr_t buffer_size) {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
return Api::NewError("%s: Cannot compile on an AOT runtime.", CURRENT_FUNC);
|
|
#else
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
StackZone zone(T);
|
|
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
// NOTE: We do not attach a finalizer for this object, because the embedder
|
|
// will/should free it once the isolate group has shutdown.
|
|
// See also http://dartbug.com/37030.
|
|
const auto& td = ExternalTypedData::Handle(ExternalTypedData::New(
|
|
kExternalTypedDataUint8ArrayCid, const_cast<uint8_t*>(buffer),
|
|
buffer_size, Heap::kOld));
|
|
|
|
const char* error = nullptr;
|
|
std::unique_ptr<kernel::Program> program =
|
|
kernel::Program::ReadFromTypedData(td, &error);
|
|
if (program == nullptr) {
|
|
return Api::NewError("Can't load Kernel binary: %s.", error);
|
|
}
|
|
const Object& result =
|
|
kernel::KernelLoader::LoadEntireProgram(program.get(), false);
|
|
program.reset();
|
|
|
|
IsolateGroupSource* source = Isolate::Current()->source();
|
|
source->add_loaded_blob(Z, td);
|
|
|
|
return Api::NewHandle(T, result.ptr());
|
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
|
}
|
|
|
|
// Finalizes classes and invokes Dart core library function that completes
|
|
// futures of loadLibrary calls (deferred library loading).
|
|
DART_EXPORT Dart_Handle Dart_FinalizeLoading(bool complete_futures) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
Isolate* I = T->isolate();
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
// Finalize all classes if needed.
|
|
Dart_Handle state = Api::CheckAndFinalizePendingClasses(T);
|
|
if (Api::IsError(state)) {
|
|
return state;
|
|
}
|
|
|
|
#if !defined(PRODUCT)
|
|
// Now that the newly loaded classes are finalized, notify the debugger
|
|
// that new code has been loaded. If there are latent breakpoints in
|
|
// the new code, the debugger convert them to unresolved source breakpoints.
|
|
// The code that completes the futures (invoked below) may call into the
|
|
// newly loaded code and trigger one of these breakpoints.
|
|
I->debugger()->NotifyDoneLoading();
|
|
#endif
|
|
|
|
// After having loaded all the code, we can let the GC set reaonsable limits
|
|
// for the heap growth.
|
|
// If this is an auxiliary isolate inside a larger isolate group, we will not
|
|
// re-initialize the growth policy.
|
|
if (I->group()->ContainsOnlyOneIsolate()) {
|
|
I->group()->heap()->old_space()->EvaluateAfterLoading();
|
|
}
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
if (FLAG_enable_mirrors) {
|
|
// Notify mirrors that MirrorSystem.libraries needs to be recomputed.
|
|
const Library& libmirrors = Library::Handle(Z, Library::MirrorsLibrary());
|
|
const Field& dirty_bit = Field::Handle(
|
|
Z, libmirrors.LookupLocalField(String::Handle(String::New("_dirty"))));
|
|
ASSERT(!dirty_bit.IsNull() && dirty_bit.is_static());
|
|
dirty_bit.SetStaticValue(Bool::True());
|
|
}
|
|
#endif
|
|
|
|
return Api::Success();
|
|
}
|
|
|
|
static Dart_Handle DeferredLoadComplete(intptr_t loading_unit_id,
|
|
bool error,
|
|
const uint8_t* snapshot_data,
|
|
const uint8_t* snapshot_instructions,
|
|
const char* error_message,
|
|
bool transient_error) {
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
auto IG = T->isolate_group();
|
|
CHECK_CALLBACK_STATE(T);
|
|
|
|
const Array& loading_units =
|
|
Array::Handle(IG->object_store()->loading_units());
|
|
if (loading_units.IsNull() || (loading_unit_id < LoadingUnit::kRootId) ||
|
|
(loading_unit_id >= loading_units.Length())) {
|
|
return Api::NewError("Invalid loading unit");
|
|
}
|
|
LoadingUnit& unit = LoadingUnit::Handle();
|
|
unit ^= loading_units.At(loading_unit_id);
|
|
if (unit.loaded()) {
|
|
return Api::NewError("Unit already loaded");
|
|
}
|
|
|
|
if (error) {
|
|
CHECK_NULL(error_message);
|
|
return Api::NewHandle(
|
|
T, unit.CompleteLoad(String::Handle(String::New(error_message)),
|
|
transient_error));
|
|
} else {
|
|
#if defined(SUPPORT_TIMELINE)
|
|
TimelineBeginEndScope tbes(T, Timeline::GetIsolateStream(),
|
|
"ReadUnitSnapshot");
|
|
#endif // defined(SUPPORT_TIMELINE)
|
|
const Snapshot* snapshot = Snapshot::SetupFromBuffer(snapshot_data);
|
|
if (snapshot == NULL) {
|
|
return Api::NewError("Invalid snapshot");
|
|
}
|
|
if (!IsSnapshotCompatible(Dart::vm_snapshot_kind(), snapshot->kind())) {
|
|
const String& message = String::Handle(String::NewFormatted(
|
|
"Incompatible snapshot kinds: vm '%s', isolate '%s'",
|
|
Snapshot::KindToCString(Dart::vm_snapshot_kind()),
|
|
Snapshot::KindToCString(snapshot->kind())));
|
|
return Api::NewHandle(T, ApiError::New(message));
|
|
}
|
|
|
|
FullSnapshotReader reader(snapshot, snapshot_instructions, T);
|
|
const Error& error = Error::Handle(reader.ReadUnitSnapshot(unit));
|
|
if (!error.IsNull()) {
|
|
return Api::NewHandle(T, error.ptr());
|
|
}
|
|
|
|
return Api::NewHandle(T, unit.CompleteLoad(String::Handle(), false));
|
|
}
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_DeferredLoadComplete(intptr_t loading_unit_id,
|
|
const uint8_t* snapshot_data,
|
|
const uint8_t* snapshot_instructions) {
|
|
return DeferredLoadComplete(loading_unit_id, false, snapshot_data,
|
|
snapshot_instructions, nullptr, false);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_DeferredLoadCompleteError(intptr_t loading_unit_id,
|
|
const char* error_message,
|
|
bool transient) {
|
|
return DeferredLoadComplete(loading_unit_id, true, nullptr, nullptr,
|
|
error_message, transient);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_SetNativeResolver(Dart_Handle library,
|
|
Dart_NativeEntryResolver resolver,
|
|
Dart_NativeEntrySymbol symbol) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Library& lib = Api::UnwrapLibraryHandle(Z, library);
|
|
if (lib.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, library, Library);
|
|
}
|
|
lib.set_native_entry_resolver(resolver);
|
|
lib.set_native_entry_symbol_resolver(symbol);
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_GetNativeResolver(Dart_Handle library,
|
|
Dart_NativeEntryResolver* resolver) {
|
|
if (resolver == NULL) {
|
|
RETURN_NULL_ERROR(resolver);
|
|
}
|
|
*resolver = NULL;
|
|
DARTSCOPE(Thread::Current());
|
|
const Library& lib = Api::UnwrapLibraryHandle(Z, library);
|
|
if (lib.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, library, Library);
|
|
}
|
|
*resolver = lib.native_entry_resolver();
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetNativeSymbol(Dart_Handle library,
|
|
Dart_NativeEntrySymbol* resolver) {
|
|
if (resolver == NULL) {
|
|
RETURN_NULL_ERROR(resolver);
|
|
}
|
|
*resolver = NULL;
|
|
DARTSCOPE(Thread::Current());
|
|
const Library& lib = Api::UnwrapLibraryHandle(Z, library);
|
|
if (lib.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, library, Library);
|
|
}
|
|
*resolver = lib.native_entry_symbol_resolver();
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_SetFfiNativeResolver(Dart_Handle library,
|
|
Dart_FfiNativeResolver resolver) {
|
|
DARTSCOPE(Thread::Current());
|
|
const Library& lib = Api::UnwrapLibraryHandle(Z, library);
|
|
if (lib.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, library, Library);
|
|
}
|
|
lib.set_ffi_native_resolver(resolver);
|
|
return Api::Success();
|
|
}
|
|
|
|
// --- Peer support ---
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetPeer(Dart_Handle object, void** peer) {
|
|
if (peer == NULL) {
|
|
RETURN_NULL_ERROR(peer);
|
|
}
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
REUSABLE_OBJECT_HANDLESCOPE(thread);
|
|
Object& obj = thread->ObjectHandle();
|
|
obj = 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);
|
|
}
|
|
{
|
|
NoSafepointScope no_safepoint;
|
|
ObjectPtr raw_obj = obj.ptr();
|
|
*peer = thread->heap()->GetPeer(raw_obj);
|
|
}
|
|
return Api::Success();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_SetPeer(Dart_Handle object, void* peer) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
TransitionNativeToVM transition(thread);
|
|
REUSABLE_OBJECT_HANDLESCOPE(thread);
|
|
Object& obj = thread->ObjectHandle();
|
|
obj = 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);
|
|
}
|
|
{
|
|
NoSafepointScope no_safepoint;
|
|
ObjectPtr raw_obj = obj.ptr();
|
|
thread->heap()->SetPeer(raw_obj, peer);
|
|
}
|
|
return Api::Success();
|
|
}
|
|
|
|
// --- Dart Front-End (Kernel) support ---
|
|
|
|
DART_EXPORT bool Dart_IsKernelIsolate(Dart_Isolate isolate) {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
return false;
|
|
#else
|
|
Isolate* iso = reinterpret_cast<Isolate*>(isolate);
|
|
return KernelIsolate::IsKernelIsolate(iso);
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT bool Dart_KernelIsolateIsRunning() {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
return false;
|
|
#else
|
|
return KernelIsolate::IsRunning();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT Dart_Port Dart_KernelPort() {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
return false;
|
|
#else
|
|
return KernelIsolate::KernelPort();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT Dart_KernelCompilationResult
|
|
Dart_CompileToKernel(const char* script_uri,
|
|
const uint8_t* platform_kernel,
|
|
intptr_t platform_kernel_size,
|
|
bool incremental_compile,
|
|
bool snapshot_compile,
|
|
const char* package_config,
|
|
Dart_KernelCompilationVerbosityLevel verbosity) {
|
|
API_TIMELINE_DURATION(Thread::Current());
|
|
|
|
Dart_KernelCompilationResult result = {};
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
result.status = Dart_KernelCompilationStatus_Unknown;
|
|
result.error = Utils::StrDup("Dart_CompileToKernel is unsupported.");
|
|
#else
|
|
result = KernelIsolate::CompileToKernel(
|
|
script_uri, platform_kernel, platform_kernel_size, 0, NULL,
|
|
incremental_compile, snapshot_compile, package_config, NULL, NULL,
|
|
FLAG_sound_null_safety, verbosity);
|
|
if (result.status == Dart_KernelCompilationStatus_Ok) {
|
|
Dart_KernelCompilationResult accept_result =
|
|
KernelIsolate::AcceptCompilation();
|
|
if (accept_result.status != Dart_KernelCompilationStatus_Ok) {
|
|
FATAL1(
|
|
"An error occurred in the CFE while accepting the most recent"
|
|
" compilation results: %s",
|
|
accept_result.error);
|
|
}
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
DART_EXPORT Dart_KernelCompilationResult
|
|
Dart_CompileToKernelWithGivenNullsafety(
|
|
const char* script_uri,
|
|
const uint8_t* platform_kernel,
|
|
intptr_t platform_kernel_size,
|
|
bool snapshot_compile,
|
|
const char* package_config,
|
|
bool null_safety,
|
|
Dart_KernelCompilationVerbosityLevel verbosity) {
|
|
API_TIMELINE_DURATION(Thread::Current());
|
|
|
|
Dart_KernelCompilationResult result = {};
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
result.status = Dart_KernelCompilationStatus_Unknown;
|
|
result.error = Utils::StrDup("Dart_CompileToKernel is unsupported.");
|
|
#else
|
|
intptr_t null_safety_option =
|
|
null_safety ? kNullSafetyOptionStrong : kNullSafetyOptionWeak;
|
|
result = KernelIsolate::CompileToKernel(
|
|
script_uri, platform_kernel, platform_kernel_size,
|
|
/*source_files_count=*/0, /*source_files=*/nullptr,
|
|
/*incremental_compile=*/false, snapshot_compile, package_config,
|
|
/*multiroot_filepaths=*/nullptr, /*multiroot_scheme=*/nullptr,
|
|
null_safety_option, verbosity);
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
DART_EXPORT Dart_KernelCompilationResult Dart_KernelListDependencies() {
|
|
Dart_KernelCompilationResult result = {};
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
result.status = Dart_KernelCompilationStatus_Unknown;
|
|
result.error = Utils::StrDup("Dart_KernelListDependencies is unsupported.");
|
|
#else
|
|
result = KernelIsolate::ListDependencies();
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
DART_EXPORT void Dart_SetDartLibrarySourcesKernel(
|
|
const uint8_t* platform_kernel,
|
|
const intptr_t platform_kernel_size) {
|
|
#if !defined(PRODUCT)
|
|
Service::SetDartLibraryKernelForSources(platform_kernel,
|
|
platform_kernel_size);
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT bool Dart_DetectNullSafety(const char* script_uri,
|
|
const char* package_config,
|
|
const char* original_working_directory,
|
|
const uint8_t* snapshot_data,
|
|
const uint8_t* snapshot_instructions,
|
|
const uint8_t* kernel_buffer,
|
|
intptr_t kernel_buffer_size) {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
ASSERT(FLAG_sound_null_safety != kNullSafetyOptionUnspecified);
|
|
return (FLAG_sound_null_safety == kNullSafetyOptionStrong);
|
|
#else
|
|
bool null_safety;
|
|
if (FLAG_sound_null_safety == kNullSafetyOptionUnspecified) {
|
|
null_safety = Dart::DetectNullSafety(
|
|
script_uri, snapshot_data, snapshot_instructions, kernel_buffer,
|
|
kernel_buffer_size, package_config, original_working_directory);
|
|
} else {
|
|
null_safety = (FLAG_sound_null_safety == kNullSafetyOptionStrong);
|
|
}
|
|
return null_safety;
|
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
|
}
|
|
|
|
// --- Service support ---
|
|
|
|
DART_EXPORT bool Dart_IsServiceIsolate(Dart_Isolate isolate) {
|
|
Isolate* iso = reinterpret_cast<Isolate*>(isolate);
|
|
return ServiceIsolate::IsServiceIsolate(iso);
|
|
}
|
|
|
|
DART_EXPORT void Dart_RegisterIsolateServiceRequestCallback(
|
|
const char* name,
|
|
Dart_ServiceRequestCallback callback,
|
|
void* user_data) {
|
|
#if !defined(PRODUCT)
|
|
Service::RegisterIsolateEmbedderCallback(name, callback, user_data);
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT void Dart_RegisterRootServiceRequestCallback(
|
|
const char* name,
|
|
Dart_ServiceRequestCallback callback,
|
|
void* user_data) {
|
|
#if !defined(PRODUCT)
|
|
Service::RegisterRootEmbedderCallback(name, callback, user_data);
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT void Dart_SetEmbedderInformationCallback(
|
|
Dart_EmbedderInformationCallback callback) {
|
|
#if !defined(PRODUCT)
|
|
Service::SetEmbedderInformationCallback(callback);
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT char* Dart_SetServiceStreamCallbacks(
|
|
Dart_ServiceStreamListenCallback listen_callback,
|
|
Dart_ServiceStreamCancelCallback cancel_callback) {
|
|
#if defined(PRODUCT)
|
|
return NULL;
|
|
#else
|
|
if (listen_callback != NULL) {
|
|
if (Service::stream_listen_callback() != NULL) {
|
|
return Utils::StrDup(
|
|
"Dart_SetServiceStreamCallbacks "
|
|
"permits only one listen callback to be registered, please "
|
|
"remove the existing callback and then add this callback");
|
|
}
|
|
} else {
|
|
if (Service::stream_listen_callback() == NULL) {
|
|
return Utils::StrDup(
|
|
"Dart_SetServiceStreamCallbacks "
|
|
"expects 'listen_callback' to be present in the callback set.");
|
|
}
|
|
}
|
|
if (cancel_callback != NULL) {
|
|
if (Service::stream_cancel_callback() != NULL) {
|
|
return Utils::StrDup(
|
|
"Dart_SetServiceStreamCallbacks "
|
|
"permits only one cancel callback to be registered, please "
|
|
"remove the existing callback and then add this callback");
|
|
}
|
|
} else {
|
|
if (Service::stream_cancel_callback() == NULL) {
|
|
return Utils::StrDup(
|
|
"Dart_SetServiceStreamCallbacks "
|
|
"expects 'cancel_callback' to be present in the callback set.");
|
|
}
|
|
}
|
|
Service::SetEmbedderStreamCallbacks(listen_callback, cancel_callback);
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT char* Dart_ServiceSendDataEvent(const char* stream_id,
|
|
const char* event_kind,
|
|
const uint8_t* bytes,
|
|
intptr_t bytes_length) {
|
|
#if !defined(PRODUCT)
|
|
if (stream_id == NULL) {
|
|
return Utils::StrDup(
|
|
"Dart_ServiceSendDataEvent expects argument 'stream_id' to be "
|
|
"non-null.");
|
|
}
|
|
if (event_kind == NULL) {
|
|
return Utils::StrDup(
|
|
"Dart_ServiceSendDataEvent expects argument 'event_kind' to be "
|
|
"non-null.");
|
|
}
|
|
if (bytes == NULL) {
|
|
return Utils::StrDup(
|
|
"Dart_ServiceSendDataEvent expects argument 'bytes' to be non-null.");
|
|
}
|
|
if (bytes_length < 0) {
|
|
return Utils::StrDup(
|
|
"Dart_ServiceSendDataEvent expects argument 'bytes_length' to be >= "
|
|
"0.");
|
|
}
|
|
Service::SendEmbedderEvent(Isolate::Current(), // May be NULL
|
|
stream_id, event_kind, bytes, bytes_length);
|
|
#endif
|
|
return nullptr;
|
|
}
|
|
|
|
DART_EXPORT void Dart_SetGCEventCallback(Dart_GCEventCallback callback) {
|
|
Dart::set_gc_event_callback(callback);
|
|
}
|
|
|
|
DART_EXPORT char* Dart_SetFileModifiedCallback(
|
|
Dart_FileModifiedCallback file_modified_callback) {
|
|
#if !defined(PRODUCT)
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
if (file_modified_callback != NULL) {
|
|
if (IsolateGroupReloadContext::file_modified_callback() != NULL) {
|
|
return Utils::StrDup(
|
|
"Dart_SetFileModifiedCallback permits only one callback to be"
|
|
" registered, please remove the existing callback and then add"
|
|
" this callback");
|
|
}
|
|
} else {
|
|
if (IsolateGroupReloadContext::file_modified_callback() == NULL) {
|
|
return Utils::StrDup(
|
|
"Dart_SetFileModifiedCallback expects 'file_modified_callback' to"
|
|
" be set before it is cleared.");
|
|
}
|
|
}
|
|
IsolateGroupReloadContext::SetFileModifiedCallback(file_modified_callback);
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
#endif // !defined(PRODUCT)
|
|
return NULL;
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsReloading() {
|
|
#if defined(PRODUCT) || defined(DART_PRECOMPILED_RUNTIME)
|
|
return false;
|
|
#else
|
|
Thread* thread = Thread::Current();
|
|
Isolate* isolate = thread->isolate();
|
|
CHECK_ISOLATE(isolate);
|
|
return isolate->group()->IsReloading();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT bool Dart_SetEnabledTimelineCategory(const char* categories) {
|
|
#if defined(SUPPORT_TIMELINE)
|
|
bool result = false;
|
|
if (categories != nullptr) {
|
|
char* carray = Utils::SCreate("[%s]", categories);
|
|
result = Service::EnableTimelineStreams(carray);
|
|
free(carray);
|
|
}
|
|
return result;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT int64_t Dart_TimelineGetMicros() {
|
|
return OS::GetCurrentMonotonicMicros();
|
|
}
|
|
|
|
DART_EXPORT int64_t Dart_TimelineGetTicks() {
|
|
return OS::GetCurrentMonotonicTicks();
|
|
}
|
|
|
|
DART_EXPORT int64_t Dart_TimelineGetTicksFrequency() {
|
|
return OS::GetCurrentMonotonicFrequency();
|
|
}
|
|
|
|
DART_EXPORT void Dart_TimelineEvent(const char* label,
|
|
int64_t timestamp0,
|
|
int64_t timestamp1_or_async_id,
|
|
Dart_Timeline_Event_Type type,
|
|
intptr_t argument_count,
|
|
const char** argument_names,
|
|
const char** argument_values) {
|
|
#if defined(SUPPORT_TIMELINE)
|
|
if (type < Dart_Timeline_Event_Begin) {
|
|
return;
|
|
}
|
|
if (type > Dart_Timeline_Event_Flow_End) {
|
|
return;
|
|
}
|
|
if (!Dart::SetActiveApiCall()) {
|
|
return;
|
|
}
|
|
TimelineStream* stream = Timeline::GetEmbedderStream();
|
|
ASSERT(stream != NULL);
|
|
TimelineEvent* event = stream->StartEvent();
|
|
if (event != NULL) {
|
|
switch (type) {
|
|
case Dart_Timeline_Event_Begin:
|
|
event->Begin(label, timestamp1_or_async_id, timestamp0);
|
|
break;
|
|
case Dart_Timeline_Event_End:
|
|
event->End(label, timestamp1_or_async_id, timestamp0);
|
|
break;
|
|
case Dart_Timeline_Event_Instant:
|
|
event->Instant(label, timestamp0);
|
|
break;
|
|
case Dart_Timeline_Event_Duration:
|
|
event->Duration(label, timestamp0, timestamp1_or_async_id);
|
|
break;
|
|
case Dart_Timeline_Event_Async_Begin:
|
|
event->AsyncBegin(label, timestamp1_or_async_id, timestamp0);
|
|
break;
|
|
case Dart_Timeline_Event_Async_End:
|
|
event->AsyncEnd(label, timestamp1_or_async_id, timestamp0);
|
|
break;
|
|
case Dart_Timeline_Event_Async_Instant:
|
|
event->AsyncInstant(label, timestamp1_or_async_id, timestamp0);
|
|
break;
|
|
case Dart_Timeline_Event_Counter:
|
|
event->Counter(label, timestamp0);
|
|
break;
|
|
case Dart_Timeline_Event_Flow_Begin:
|
|
event->FlowBegin(label, timestamp1_or_async_id, timestamp0);
|
|
break;
|
|
case Dart_Timeline_Event_Flow_Step:
|
|
event->FlowStep(label, timestamp1_or_async_id, timestamp0);
|
|
break;
|
|
case Dart_Timeline_Event_Flow_End:
|
|
event->FlowEnd(label, timestamp1_or_async_id, timestamp0);
|
|
break;
|
|
default:
|
|
FATAL("Unknown Dart_Timeline_Event_Type");
|
|
}
|
|
event->SetNumArguments(argument_count);
|
|
for (intptr_t i = 0; i < argument_count; i++) {
|
|
event->CopyArgument(i, argument_names[i], argument_values[i]);
|
|
}
|
|
event->Complete();
|
|
}
|
|
Dart::ResetActiveApiCall();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT void Dart_SetThreadName(const char* name) {
|
|
OSThread* thread = OSThread::Current();
|
|
if (thread == NULL) {
|
|
// VM is shutting down.
|
|
return;
|
|
}
|
|
thread->SetName(name);
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_SortClasses() {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
return Api::NewError("%s: Cannot compile on an AOT runtime.", CURRENT_FUNC);
|
|
#else
|
|
DARTSCOPE(Thread::Current());
|
|
|
|
// Prevent background compiler from running while code is being cleared and
|
|
// adding new code.
|
|
NoBackgroundCompilerScope no_bg_compiler(T);
|
|
|
|
// We don't have mechanisms to change class-ids that are embedded in code and
|
|
// ICData.
|
|
ClassFinalizer::ClearAllCode();
|
|
// Make sure that ICData etc. that have been cleared are also removed from
|
|
// the heap so that they are not found by the heap verifier.
|
|
IsolateGroup::Current()->heap()->CollectAllGarbage();
|
|
ClassFinalizer::SortClasses();
|
|
return Api::Success();
|
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_Precompile() {
|
|
#if defined(TARGET_ARCH_IA32)
|
|
return Api::NewError("AOT compilation is not supported on IA32.");
|
|
#elif !defined(DART_PRECOMPILER)
|
|
return Api::NewError(
|
|
"This VM was built without support for AOT compilation.");
|
|
#else
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_BEGIN_END(T);
|
|
if (!FLAG_precompiled_mode) {
|
|
return Api::NewError("Flag --precompilation was not specified.");
|
|
}
|
|
Dart_Handle result = Api::CheckAndFinalizePendingClasses(T);
|
|
if (Api::IsError(result)) {
|
|
return result;
|
|
}
|
|
CHECK_CALLBACK_STATE(T);
|
|
CompilerState state(Thread::Current(), /*is_aot=*/true,
|
|
/*is_optimizing=*/true);
|
|
CHECK_ERROR_HANDLE(Precompiler::CompileAll());
|
|
return Api::Success();
|
|
#endif
|
|
}
|
|
|
|
// Used for StreamingWriteStream/BlobImageWriter sizes for ELF and blobs.
|
|
#if !defined(TARGET_ARCH_IA32) && defined(DART_PRECOMPILER)
|
|
static const intptr_t kAssemblyInitialSize = 512 * KB;
|
|
static const intptr_t kInitialSize = 2 * MB;
|
|
static const intptr_t kInitialDebugSize = 1 * MB;
|
|
|
|
static void CreateAppAOTSnapshot(
|
|
Dart_StreamingWriteCallback callback,
|
|
void* callback_data,
|
|
bool strip,
|
|
bool as_elf,
|
|
void* debug_callback_data,
|
|
GrowableArray<LoadingUnitSerializationData*>* units,
|
|
LoadingUnitSerializationData* unit,
|
|
uint32_t program_hash) {
|
|
Thread* T = Thread::Current();
|
|
|
|
NOT_IN_PRODUCT(TimelineBeginEndScope tbes2(T, Timeline::GetIsolateStream(),
|
|
"WriteAppAOTSnapshot"));
|
|
|
|
ZoneWriteStream vm_snapshot_data(T->zone(), FullSnapshotWriter::kInitialSize);
|
|
ZoneWriteStream vm_snapshot_instructions(T->zone(), kInitialSize);
|
|
ZoneWriteStream isolate_snapshot_data(T->zone(),
|
|
FullSnapshotWriter::kInitialSize);
|
|
ZoneWriteStream isolate_snapshot_instructions(T->zone(), kInitialSize);
|
|
|
|
const bool generate_debug = debug_callback_data != nullptr;
|
|
|
|
if (as_elf) {
|
|
StreamingWriteStream elf_stream(kInitialSize, callback, callback_data);
|
|
StreamingWriteStream debug_stream(generate_debug ? kInitialDebugSize : 0,
|
|
callback, debug_callback_data);
|
|
|
|
auto const dwarf = strip ? nullptr : new (Z) Dwarf(Z);
|
|
auto const elf = new (Z) Elf(Z, &elf_stream, Elf::Type::Snapshot, dwarf);
|
|
// Re-use the same DWARF object if the snapshot is unstripped.
|
|
auto const debug_elf =
|
|
generate_debug ? new (Z) Elf(Z, &debug_stream, Elf::Type::DebugInfo,
|
|
strip ? new (Z) Dwarf(Z) : dwarf)
|
|
: nullptr;
|
|
|
|
BlobImageWriter image_writer(T, &vm_snapshot_instructions,
|
|
&isolate_snapshot_instructions, debug_elf,
|
|
elf);
|
|
FullSnapshotWriter writer(Snapshot::kFullAOT, &vm_snapshot_data,
|
|
&isolate_snapshot_data, &image_writer,
|
|
&image_writer);
|
|
|
|
if (unit == nullptr || unit->id() == LoadingUnit::kRootId) {
|
|
writer.WriteFullSnapshot(units);
|
|
} else {
|
|
writer.WriteUnitSnapshot(units, unit, program_hash);
|
|
}
|
|
|
|
elf->Finalize();
|
|
if (debug_elf != nullptr) {
|
|
debug_elf->Finalize();
|
|
}
|
|
} else {
|
|
StreamingWriteStream assembly_stream(kAssemblyInitialSize, callback,
|
|
callback_data);
|
|
StreamingWriteStream debug_stream(generate_debug ? kInitialDebugSize : 0,
|
|
callback, debug_callback_data);
|
|
|
|
auto const elf = generate_debug
|
|
? new (Z) Elf(Z, &debug_stream, Elf::Type::DebugInfo,
|
|
new (Z) Dwarf(Z))
|
|
: nullptr;
|
|
|
|
AssemblyImageWriter image_writer(T, &assembly_stream, strip, elf);
|
|
FullSnapshotWriter writer(Snapshot::kFullAOT, &vm_snapshot_data,
|
|
&isolate_snapshot_data, &image_writer,
|
|
&image_writer);
|
|
|
|
if (unit == nullptr || unit->id() == LoadingUnit::kRootId) {
|
|
writer.WriteFullSnapshot(units);
|
|
} else {
|
|
writer.WriteUnitSnapshot(units, unit, program_hash);
|
|
}
|
|
image_writer.Finalize();
|
|
}
|
|
}
|
|
|
|
static void Split(Dart_CreateLoadingUnitCallback next_callback,
|
|
void* next_callback_data,
|
|
bool strip,
|
|
bool as_elf,
|
|
Dart_StreamingWriteCallback write_callback,
|
|
Dart_StreamingCloseCallback close_callback) {
|
|
Thread* T = Thread::Current();
|
|
ProgramVisitor::AssignUnits(T);
|
|
|
|
const Array& loading_units =
|
|
Array::Handle(T->isolate_group()->object_store()->loading_units());
|
|
const uint32_t program_hash = ProgramVisitor::Hash(T);
|
|
loading_units.SetAt(0, Smi::Handle(Z, Smi::New(program_hash)));
|
|
GrowableArray<LoadingUnitSerializationData*> data;
|
|
data.SetLength(loading_units.Length());
|
|
data[0] = nullptr;
|
|
|
|
LoadingUnit& loading_unit = LoadingUnit::Handle();
|
|
LoadingUnit& parent = LoadingUnit::Handle();
|
|
for (intptr_t id = 1; id < loading_units.Length(); id++) {
|
|
loading_unit ^= loading_units.At(id);
|
|
parent = loading_unit.parent();
|
|
LoadingUnitSerializationData* parent_data =
|
|
parent.IsNull() ? nullptr : data[parent.id()];
|
|
data[id] = new LoadingUnitSerializationData(id, parent_data);
|
|
}
|
|
|
|
for (intptr_t id = 1; id < loading_units.Length(); id++) {
|
|
void* write_callback_data = nullptr;
|
|
void* write_debug_callback_data = nullptr;
|
|
{
|
|
TransitionVMToNative transition(T);
|
|
next_callback(next_callback_data, id, &write_callback_data,
|
|
&write_debug_callback_data);
|
|
}
|
|
CreateAppAOTSnapshot(write_callback, write_callback_data, strip, as_elf,
|
|
write_debug_callback_data, &data, data[id],
|
|
program_hash);
|
|
{
|
|
TransitionVMToNative transition(T);
|
|
close_callback(write_callback_data);
|
|
if (write_debug_callback_data != nullptr) {
|
|
close_callback(write_debug_callback_data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_CreateAppAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback,
|
|
void* callback_data,
|
|
bool strip,
|
|
void* debug_callback_data) {
|
|
#if defined(TARGET_ARCH_IA32)
|
|
return Api::NewError("AOT compilation is not supported on IA32.");
|
|
#elif defined(DART_TARGET_OS_WINDOWS)
|
|
return Api::NewError("Assembly generation is not implemented for Windows.");
|
|
#elif !defined(DART_PRECOMPILER)
|
|
return Api::NewError(
|
|
"This VM was built without support for AOT compilation.");
|
|
#else
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
CHECK_NULL(callback);
|
|
|
|
// Mark as not split.
|
|
T->isolate_group()->object_store()->set_loading_units(Object::null_array());
|
|
|
|
CreateAppAOTSnapshot(callback, callback_data, strip, /*as_elf*/ false,
|
|
debug_callback_data, nullptr, nullptr, 0);
|
|
|
|
return Api::Success();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_CreateAppAOTSnapshotAsAssemblies(
|
|
Dart_CreateLoadingUnitCallback next_callback,
|
|
void* next_callback_data,
|
|
bool strip,
|
|
Dart_StreamingWriteCallback write_callback,
|
|
Dart_StreamingCloseCallback close_callback) {
|
|
#if defined(TARGET_ARCH_IA32)
|
|
return Api::NewError("AOT compilation is not supported on IA32.");
|
|
#elif defined(DART_TARGET_OS_WINDOWS)
|
|
return Api::NewError("Assembly generation is not implemented for Windows.");
|
|
#elif !defined(DART_PRECOMPILER)
|
|
return Api::NewError(
|
|
"This VM was built without support for AOT compilation.");
|
|
#else
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
CHECK_NULL(next_callback);
|
|
CHECK_NULL(write_callback);
|
|
CHECK_NULL(close_callback);
|
|
|
|
Split(next_callback, next_callback_data, strip, /*as_elf*/ false,
|
|
write_callback, close_callback);
|
|
|
|
return Api::Success();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_CreateVMAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback,
|
|
void* callback_data) {
|
|
#if defined(TARGET_ARCH_IA32)
|
|
return Api::NewError("AOT compilation is not supported on IA32.");
|
|
#elif defined(DART_TARGET_OS_WINDOWS)
|
|
return Api::NewError("Assembly generation is not implemented for Windows.");
|
|
#elif !defined(DART_PRECOMPILER)
|
|
return Api::NewError(
|
|
"This VM was built without support for AOT compilation.");
|
|
#else
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
CHECK_NULL(callback);
|
|
|
|
TIMELINE_DURATION(T, Isolate, "WriteVMAOTSnapshot");
|
|
StreamingWriteStream assembly_stream(kAssemblyInitialSize, callback,
|
|
callback_data);
|
|
AssemblyImageWriter image_writer(T, &assembly_stream);
|
|
ZoneWriteStream vm_snapshot_data(T->zone(), FullSnapshotWriter::kInitialSize);
|
|
FullSnapshotWriter writer(Snapshot::kFullAOT, &vm_snapshot_data, nullptr,
|
|
&image_writer, nullptr);
|
|
|
|
writer.WriteFullSnapshot();
|
|
|
|
return Api::Success();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_CreateAppAOTSnapshotAsElf(Dart_StreamingWriteCallback callback,
|
|
void* callback_data,
|
|
bool strip,
|
|
void* debug_callback_data) {
|
|
#if defined(TARGET_ARCH_IA32)
|
|
return Api::NewError("AOT compilation is not supported on IA32.");
|
|
#elif !defined(DART_PRECOMPILER)
|
|
return Api::NewError(
|
|
"This VM was built without support for AOT compilation.");
|
|
#else
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
CHECK_NULL(callback);
|
|
|
|
// Mark as not split.
|
|
T->isolate_group()->object_store()->set_loading_units(Object::null_array());
|
|
|
|
CreateAppAOTSnapshot(callback, callback_data, strip, /*as_elf*/ true,
|
|
debug_callback_data, nullptr, nullptr, 0);
|
|
|
|
return Api::Success();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_CreateAppAOTSnapshotAsElfs(Dart_CreateLoadingUnitCallback next_callback,
|
|
void* next_callback_data,
|
|
bool strip,
|
|
Dart_StreamingWriteCallback write_callback,
|
|
Dart_StreamingCloseCallback close_callback) {
|
|
#if defined(TARGET_ARCH_IA32)
|
|
return Api::NewError("AOT compilation is not supported on IA32.");
|
|
#elif !defined(DART_PRECOMPILER)
|
|
return Api::NewError(
|
|
"This VM was built without support for AOT compilation.");
|
|
#else
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
CHECK_NULL(next_callback);
|
|
CHECK_NULL(write_callback);
|
|
CHECK_NULL(close_callback);
|
|
|
|
Split(next_callback, next_callback_data, strip, /*as_elf*/ true,
|
|
write_callback, close_callback);
|
|
|
|
return Api::Success();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_LoadingUnitLibraryUris(intptr_t loading_unit_id) {
|
|
#if defined(TARGET_ARCH_IA32)
|
|
return Api::NewError("AOT compilation is not supported on IA32.");
|
|
#elif !defined(DART_PRECOMPILER)
|
|
return Api::NewError(
|
|
"This VM was built without support for AOT compilation.");
|
|
#else
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
|
|
const Array& loading_units =
|
|
Array::Handle(Z, T->isolate_group()->object_store()->loading_unit_uris());
|
|
if (loading_unit_id >= 0 && loading_unit_id < loading_units.Length()) {
|
|
return Api::NewHandle(T, loading_units.At(loading_unit_id));
|
|
}
|
|
return Api::NewError("Invalid loading_unit_id");
|
|
#endif
|
|
}
|
|
|
|
#if (!defined(TARGET_ARCH_IA32) && !defined(DART_PRECOMPILED_RUNTIME))
|
|
|
|
// Any flag that affects how we compile code might cause a problem when the
|
|
// snapshot writer generates code with one value of the flag and the snapshot
|
|
// reader expects code to behave according to another value of the flag.
|
|
// Normally, we add these flags to Dart::FeaturesString and refuse to run the
|
|
// snapshot it they don't match, but since --interpret-irregexp affects only
|
|
// 2 functions we choose to remove the code instead. See issue #34422.
|
|
static void DropRegExpMatchCode(Zone* zone) {
|
|
const String& execute_match_name =
|
|
String::Handle(zone, String::New("_ExecuteMatch"));
|
|
const String& execute_match_sticky_name =
|
|
String::Handle(zone, String::New("_ExecuteMatchSticky"));
|
|
|
|
const Library& core_lib = Library::Handle(zone, Library::CoreLibrary());
|
|
const Class& reg_exp_class =
|
|
Class::Handle(zone, core_lib.LookupClassAllowPrivate(Symbols::_RegExp()));
|
|
ASSERT(!reg_exp_class.IsNull());
|
|
|
|
auto thread = Thread::Current();
|
|
Function& func = Function::Handle(
|
|
zone, reg_exp_class.LookupFunctionAllowPrivate(execute_match_name));
|
|
ASSERT(!func.IsNull());
|
|
Code& code = Code::Handle(zone);
|
|
SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
|
|
if (func.HasCode()) {
|
|
code = func.CurrentCode();
|
|
ASSERT(!code.IsNull());
|
|
code.DisableDartCode();
|
|
}
|
|
func.ClearCode();
|
|
func.ClearICDataArray();
|
|
ASSERT(!func.HasCode());
|
|
|
|
func = reg_exp_class.LookupFunctionAllowPrivate(execute_match_sticky_name);
|
|
ASSERT(!func.IsNull());
|
|
if (func.HasCode()) {
|
|
code = func.CurrentCode();
|
|
ASSERT(!code.IsNull());
|
|
code.DisableDartCode();
|
|
}
|
|
func.ClearCode();
|
|
func.ClearICDataArray();
|
|
ASSERT(!func.HasCode());
|
|
}
|
|
|
|
#endif // (!defined(TARGET_ARCH_IA32) && !defined(DART_PRECOMPILED_RUNTIME))
|
|
|
|
DART_EXPORT Dart_Handle Dart_CreateCoreJITSnapshotAsBlobs(
|
|
uint8_t** vm_snapshot_data_buffer,
|
|
intptr_t* vm_snapshot_data_size,
|
|
uint8_t** vm_snapshot_instructions_buffer,
|
|
intptr_t* vm_snapshot_instructions_size,
|
|
uint8_t** isolate_snapshot_data_buffer,
|
|
intptr_t* isolate_snapshot_data_size,
|
|
uint8_t** isolate_snapshot_instructions_buffer,
|
|
intptr_t* isolate_snapshot_instructions_size) {
|
|
#if defined(TARGET_ARCH_IA32)
|
|
return Api::NewError("Snapshots with code are not supported on IA32.");
|
|
#elif defined(DART_PRECOMPILED_RUNTIME)
|
|
return Api::NewError("JIT app snapshots cannot be taken from an AOT runtime");
|
|
#else
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
CHECK_NULL(vm_snapshot_data_buffer);
|
|
CHECK_NULL(vm_snapshot_data_size);
|
|
CHECK_NULL(vm_snapshot_instructions_buffer);
|
|
CHECK_NULL(vm_snapshot_instructions_size);
|
|
CHECK_NULL(isolate_snapshot_data_buffer);
|
|
CHECK_NULL(isolate_snapshot_data_size);
|
|
CHECK_NULL(isolate_snapshot_instructions_buffer);
|
|
CHECK_NULL(isolate_snapshot_instructions_size);
|
|
// Finalize all classes if needed.
|
|
Dart_Handle state = Api::CheckAndFinalizePendingClasses(T);
|
|
if (Api::IsError(state)) {
|
|
return state;
|
|
}
|
|
|
|
NoBackgroundCompilerScope no_bg_compiler(T);
|
|
|
|
DropRegExpMatchCode(Z);
|
|
|
|
ProgramVisitor::Dedup(T);
|
|
|
|
TIMELINE_DURATION(T, Isolate, "WriteCoreJITSnapshot");
|
|
ZoneWriteStream vm_snapshot_data(Api::TopScope(T)->zone(),
|
|
FullSnapshotWriter::kInitialSize);
|
|
ZoneWriteStream vm_snapshot_instructions(Api::TopScope(T)->zone(),
|
|
FullSnapshotWriter::kInitialSize);
|
|
ZoneWriteStream isolate_snapshot_data(Api::TopScope(T)->zone(),
|
|
FullSnapshotWriter::kInitialSize);
|
|
ZoneWriteStream isolate_snapshot_instructions(
|
|
Api::TopScope(T)->zone(), FullSnapshotWriter::kInitialSize);
|
|
|
|
BlobImageWriter image_writer(T, &vm_snapshot_instructions,
|
|
&isolate_snapshot_instructions);
|
|
FullSnapshotWriter writer(Snapshot::kFullJIT, &vm_snapshot_data,
|
|
&isolate_snapshot_data, &image_writer,
|
|
&image_writer);
|
|
writer.WriteFullSnapshot();
|
|
|
|
*vm_snapshot_data_buffer = vm_snapshot_data.buffer();
|
|
*vm_snapshot_data_size = vm_snapshot_data.bytes_written();
|
|
*vm_snapshot_instructions_buffer = vm_snapshot_instructions.buffer();
|
|
*vm_snapshot_instructions_size = vm_snapshot_instructions.bytes_written();
|
|
*isolate_snapshot_data_buffer = isolate_snapshot_data.buffer();
|
|
*isolate_snapshot_data_size = isolate_snapshot_data.bytes_written();
|
|
*isolate_snapshot_instructions_buffer =
|
|
isolate_snapshot_instructions.buffer();
|
|
*isolate_snapshot_instructions_size =
|
|
isolate_snapshot_instructions.bytes_written();
|
|
|
|
return Api::Success();
|
|
#endif
|
|
}
|
|
|
|
#if !defined(TARGET_ARCH_IA32) && !defined(DART_PRECOMPILED_RUNTIME)
|
|
static void KillNonMainIsolatesSlow(Thread* thread, Isolate* main_isolate) {
|
|
auto group = main_isolate->group();
|
|
while (true) {
|
|
bool non_main_isolates_alive = false;
|
|
{
|
|
DeoptSafepointOperationScope safepoint(thread);
|
|
group->ForEachIsolate(
|
|
[&](Isolate* isolate) {
|
|
if (isolate != main_isolate) {
|
|
Isolate::KillIfExists(isolate, Isolate::kKillMsg);
|
|
non_main_isolates_alive = true;
|
|
}
|
|
},
|
|
/*at_safepoint=*/true);
|
|
if (!non_main_isolates_alive) {
|
|
break;
|
|
}
|
|
}
|
|
OS::SleepMicros(10 * 1000);
|
|
}
|
|
}
|
|
#endif // !defined(TARGET_ARCH_IA32) && !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
DART_EXPORT Dart_Handle
|
|
Dart_CreateAppJITSnapshotAsBlobs(uint8_t** isolate_snapshot_data_buffer,
|
|
intptr_t* isolate_snapshot_data_size,
|
|
uint8_t** isolate_snapshot_instructions_buffer,
|
|
intptr_t* isolate_snapshot_instructions_size) {
|
|
#if defined(TARGET_ARCH_IA32)
|
|
return Api::NewError("Snapshots with code are not supported on IA32.");
|
|
#elif defined(DART_PRECOMPILED_RUNTIME)
|
|
return Api::NewError("JIT app snapshots cannot be taken from an AOT runtime");
|
|
#else
|
|
DARTSCOPE(Thread::Current());
|
|
API_TIMELINE_DURATION(T);
|
|
auto I = T->isolate();
|
|
auto IG = T->isolate_group();
|
|
CHECK_NULL(isolate_snapshot_data_buffer);
|
|
CHECK_NULL(isolate_snapshot_data_size);
|
|
CHECK_NULL(isolate_snapshot_instructions_buffer);
|
|
CHECK_NULL(isolate_snapshot_instructions_size);
|
|
|
|
// Finalize all classes if needed.
|
|
Dart_Handle state = Api::CheckAndFinalizePendingClasses(T);
|
|
if (Api::IsError(state)) {
|
|
return state;
|
|
}
|
|
|
|
// Kill off any auxiliary isolates before starting with deduping.
|
|
KillNonMainIsolatesSlow(T, I);
|
|
|
|
NoBackgroundCompilerScope no_bg_compiler(T);
|
|
DropRegExpMatchCode(Z);
|
|
|
|
ProgramVisitor::Dedup(T);
|
|
|
|
if (FLAG_dump_tables) {
|
|
Symbols::DumpTable(IG);
|
|
DumpTypeTable(I);
|
|
DumpTypeParameterTable(I);
|
|
DumpTypeArgumentsTable(I);
|
|
}
|
|
|
|
TIMELINE_DURATION(T, Isolate, "WriteAppJITSnapshot");
|
|
ZoneWriteStream isolate_snapshot_data(Api::TopScope(T)->zone(),
|
|
FullSnapshotWriter::kInitialSize);
|
|
ZoneWriteStream isolate_snapshot_instructions(
|
|
Api::TopScope(T)->zone(), FullSnapshotWriter::kInitialSize);
|
|
BlobImageWriter image_writer(T, /*vm_instructions=*/nullptr,
|
|
&isolate_snapshot_instructions);
|
|
FullSnapshotWriter writer(Snapshot::kFullJIT, nullptr, &isolate_snapshot_data,
|
|
nullptr, &image_writer);
|
|
writer.WriteFullSnapshot();
|
|
|
|
*isolate_snapshot_data_buffer = isolate_snapshot_data.buffer();
|
|
*isolate_snapshot_data_size = isolate_snapshot_data.bytes_written();
|
|
*isolate_snapshot_instructions_buffer =
|
|
isolate_snapshot_instructions.buffer();
|
|
*isolate_snapshot_instructions_size =
|
|
isolate_snapshot_instructions.bytes_written();
|
|
|
|
return Api::Success();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetObfuscationMap(uint8_t** buffer,
|
|
intptr_t* buffer_length) {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
return Api::NewError("No obfuscation map to save on an AOT runtime.");
|
|
#elif !defined(DART_PRECOMPILER)
|
|
return Api::NewError("Obfuscation is only supported for AOT compiler.");
|
|
#else
|
|
Thread* thread = Thread::Current();
|
|
DARTSCOPE(thread);
|
|
auto isolate_group = thread->isolate_group();
|
|
|
|
if (buffer == NULL) {
|
|
RETURN_NULL_ERROR(buffer);
|
|
}
|
|
if (buffer_length == NULL) {
|
|
RETURN_NULL_ERROR(buffer_length);
|
|
}
|
|
|
|
// Note: can't use JSONStream in PRODUCT builds.
|
|
const intptr_t kInitialBufferSize = 1 * MB;
|
|
TextBuffer text_buffer(kInitialBufferSize);
|
|
|
|
text_buffer.AddChar('[');
|
|
if (isolate_group->obfuscation_map() != nullptr) {
|
|
for (intptr_t i = 0; isolate_group->obfuscation_map()[i] != nullptr; i++) {
|
|
if (i > 0) {
|
|
text_buffer.AddChar(',');
|
|
}
|
|
text_buffer.AddChar('"');
|
|
text_buffer.AddEscapedString(isolate_group->obfuscation_map()[i]);
|
|
text_buffer.AddChar('"');
|
|
}
|
|
}
|
|
text_buffer.AddChar(']');
|
|
|
|
*buffer_length = text_buffer.length();
|
|
*reinterpret_cast<char**>(buffer) = text_buffer.Steal();
|
|
return Api::Success();
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT bool Dart_IsPrecompiledRuntime() {
|
|
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT void Dart_DumpNativeStackTrace(void* context) {
|
|
#if !defined(PRODUCT) || defined(DART_PRECOMPILER)
|
|
Profiler::DumpStackTrace(context);
|
|
#endif
|
|
}
|
|
|
|
DART_EXPORT void Dart_PrepareToAbort() {
|
|
OS::PrepareToAbort();
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetCurrentUserTag() {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
DARTSCOPE(thread);
|
|
Isolate* isolate = thread->isolate();
|
|
return Api::NewHandle(thread, isolate->current_tag());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_GetDefaultUserTag() {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
DARTSCOPE(thread);
|
|
Isolate* isolate = thread->isolate();
|
|
return Api::NewHandle(thread, isolate->default_tag());
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_NewUserTag(const char* label) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
DARTSCOPE(thread);
|
|
if (label == nullptr) {
|
|
return Api::NewError(
|
|
"Dart_NewUserTag expects argument 'label' to be non-null");
|
|
}
|
|
const String& value = String::Handle(String::New(label));
|
|
return Api::NewHandle(thread, UserTag::New(value));
|
|
}
|
|
|
|
DART_EXPORT Dart_Handle Dart_SetCurrentUserTag(Dart_Handle user_tag) {
|
|
Thread* thread = Thread::Current();
|
|
CHECK_ISOLATE(thread->isolate());
|
|
DARTSCOPE(thread);
|
|
const UserTag& tag = Api::UnwrapUserTagHandle(Z, user_tag);
|
|
if (tag.IsNull()) {
|
|
RETURN_TYPE_ERROR(Z, user_tag, UserTag);
|
|
}
|
|
return Api::NewHandle(thread, tag.MakeActive());
|
|
}
|
|
|
|
DART_EXPORT char* Dart_GetUserTagLabel(Dart_Handle user_tag) {
|
|
DARTSCOPE(Thread::Current());
|
|
const UserTag& tag = Api::UnwrapUserTagHandle(Z, user_tag);
|
|
if (tag.IsNull()) {
|
|
return nullptr;
|
|
}
|
|
const String& label = String::Handle(Z, tag.label());
|
|
return Utils::StrDup(label.ToCString());
|
|
}
|
|
|
|
} // namespace dart
|