| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #include "bin/dartutils.h" | |
| #include "bin/crypto.h" | |
| #include "bin/directory.h" | |
| #include "bin/file.h" | |
| #include "bin/io_buffer.h" | |
| #include "bin/typed_data_utils.h" | |
| #include "bin/utils.h" | |
| #include "include/dart_api.h" | |
| #include "include/dart_native_api.h" | |
| #include "platform/assert.h" | |
| #include "platform/globals.h" | |
| #include "platform/mach_o.h" | |
| #include "platform/utils.h" | |
| namespace dart { | |
| namespace bin { | |
| const char* DartUtils::original_working_directory = nullptr; | |
| dart::SimpleHashMap* DartUtils::environment_ = nullptr; | |
| MagicNumberData appjit_magic_number = {8, {0xdc, 0xdc, 0xf6, 0xf6, 0, 0, 0, 0}}; | |
| MagicNumberData aotelf_magic_number = {4, {0x7F, 0x45, 0x4C, 0x46, 0x0}}; | |
| MagicNumberData aotpe_magic_number = {2, {0x4d, 0x5a}}; | |
| MagicNumberData aotcoff_arm32_magic_number = {2, {0x01, 0xC0}}; | |
| MagicNumberData aotcoff_arm64_magic_number = {2, {0xAA, 0x64}}; | |
| MagicNumberData aotcoff_riscv32_magic_number = {2, {0x50, 0x32}}; | |
| MagicNumberData aotcoff_riscv64_magic_number = {2, {0x50, 0x64}}; | |
| MagicNumberData kernel_magic_number = {4, {0x90, 0xab, 0xcd, 0xef}}; | |
| MagicNumberData kernel_list_magic_number = { | |
| 7, | |
| {0x23, 0x40, 0x64, 0x69, 0x6c, 0x6c, 0x0a}}; // #@dill\n | |
| MagicNumberData bytecode_magic_number = {4, {0x33, 0x43, 0x42, 0x44}}; | |
| MagicNumberData gzip_magic_number = {2, {0x1f, 0x8b, 0, 0}}; | |
| static bool IsWindowsHost() { | |
| #if defined(DART_HOST_OS_WINDOWS) | |
| return true; | |
| #else // defined(DART_HOST_OS_WINDOWS) | |
| return false; | |
| #endif // defined(DART_HOST_OS_WINDOWS) | |
| } | |
| Dart_Handle CommandLineOptions::CreateRuntimeOptions() { | |
| Dart_Handle string_type = DartUtils::GetDartType("dart:core", "String"); | |
| if (Dart_IsError(string_type)) { | |
| return string_type; | |
| } | |
| Dart_Handle dart_arguments = | |
| Dart_NewListOfTypeFilled(string_type, Dart_EmptyString(), count_); | |
| if (Dart_IsError(dart_arguments)) { | |
| return dart_arguments; | |
| } | |
| for (int i = 0; i < count_; i++) { | |
| Dart_Handle argument_value = DartUtils::NewString(GetArgument(i)); | |
| if (Dart_IsError(argument_value)) { | |
| return argument_value; | |
| } | |
| Dart_Handle result = Dart_ListSetAt(dart_arguments, i, argument_value); | |
| if (Dart_IsError(result)) { | |
| return result; | |
| } | |
| } | |
| return dart_arguments; | |
| } | |
| int64_t DartUtils::GetIntegerValue(Dart_Handle value_obj) { | |
| int64_t value = 0; | |
| Dart_Handle result = Dart_IntegerToInt64(value_obj, &value); | |
| if (Dart_IsError(result)) { | |
| Dart_PropagateError(result); | |
| } | |
| return value; | |
| } | |
| int64_t DartUtils::GetInt64ValueCheckRange(Dart_Handle value_obj, | |
| int64_t lower, | |
| int64_t upper) { | |
| int64_t value = DartUtils::GetIntegerValue(value_obj); | |
| if (value < lower || upper < value) { | |
| Dart_PropagateError(Dart_NewApiError("Value outside expected range")); | |
| } | |
| return value; | |
| } | |
| intptr_t DartUtils::GetIntptrValue(Dart_Handle value_obj) { | |
| int64_t value = 0; | |
| Dart_Handle result = Dart_IntegerToInt64(value_obj, &value); | |
| if (Dart_IsError(result)) { | |
| Dart_PropagateError(result); | |
| } | |
| if (value < kIntptrMin || kIntptrMax < value) { | |
| Dart_PropagateError(Dart_NewApiError("Value outside intptr_t range")); | |
| } | |
| return static_cast<intptr_t>(value); | |
| } | |
| bool DartUtils::GetInt64Value(Dart_Handle value_obj, int64_t* value) { | |
| bool valid = Dart_IsInteger(value_obj); | |
| if (valid) { | |
| Dart_Handle result = Dart_IntegerFitsIntoInt64(value_obj, &valid); | |
| if (Dart_IsError(result)) { | |
| Dart_PropagateError(result); | |
| } | |
| } | |
| if (!valid) return false; | |
| Dart_Handle result = Dart_IntegerToInt64(value_obj, value); | |
| if (Dart_IsError(result)) { | |
| Dart_PropagateError(result); | |
| } | |
| return true; | |
| } | |
| const char* DartUtils::GetStringValue(Dart_Handle str_obj) { | |
| const char* cstring = nullptr; | |
| Dart_Handle result = Dart_StringToCString(str_obj, &cstring); | |
| if (Dart_IsError(result)) { | |
| Dart_PropagateError(result); | |
| } | |
| return cstring; | |
| } | |
| bool DartUtils::GetBooleanValue(Dart_Handle bool_obj) { | |
| bool value = false; | |
| Dart_Handle result = Dart_BooleanValue(bool_obj, &value); | |
| if (Dart_IsError(result)) { | |
| Dart_PropagateError(result); | |
| } | |
| return value; | |
| } | |
| bool DartUtils::GetNativeBooleanArgument(Dart_NativeArguments args, | |
| intptr_t index) { | |
| bool value = false; | |
| Dart_Handle result = Dart_GetNativeBooleanArgument(args, index, &value); | |
| if (Dart_IsError(result)) { | |
| Dart_PropagateError(result); | |
| } | |
| return value; | |
| } | |
| int64_t DartUtils::GetNativeIntegerArgument(Dart_NativeArguments args, | |
| intptr_t index) { | |
| int64_t value = 0; | |
| Dart_Handle result = Dart_GetNativeIntegerArgument(args, index, &value); | |
| if (Dart_IsError(result)) { | |
| Dart_PropagateError(result); | |
| } | |
| return value; | |
| } | |
| intptr_t DartUtils::GetNativeIntptrArgument(Dart_NativeArguments args, | |
| intptr_t index) { | |
| int64_t value = GetNativeIntegerArgument(args, index); | |
| if (value < kIntptrMin || kIntptrMax < value) { | |
| Dart_PropagateError(Dart_NewApiError("Value outside intptr_t range")); | |
| } | |
| return static_cast<intptr_t>(value); | |
| } | |
| const char* DartUtils::GetNativeStringArgument(Dart_NativeArguments args, | |
| intptr_t index) { | |
| char* tmp = nullptr; | |
| Dart_Handle result = | |
| Dart_GetNativeStringArgument(args, index, reinterpret_cast<void**>(&tmp)); | |
| if (Dart_IsError(result)) { | |
| Dart_PropagateError(result); | |
| } | |
| if (tmp != nullptr) { | |
| return tmp; | |
| } | |
| const char* cstring = nullptr; | |
| result = Dart_StringToCString(result, &cstring); | |
| if (Dart_IsError(result)) { | |
| Dart_PropagateError(result); | |
| } | |
| ASSERT(cstring != nullptr); | |
| return cstring; | |
| } | |
| const char* DartUtils::GetNativeTypedDataArgument(Dart_NativeArguments args, | |
| intptr_t index) { | |
| Dart_Handle handle = Dart_GetNativeArgument(args, index); | |
| TypedDataScope data(handle); | |
| ASSERT(data.type() == Dart_TypedData_kUint8); | |
| return data.GetScopedCString(); | |
| } | |
| Dart_Handle DartUtils::SetIntegerField(Dart_Handle handle, | |
| const char* name, | |
| int64_t val) { | |
| return Dart_SetField(handle, NewString(name), Dart_NewInteger(val)); | |
| } | |
| Dart_Handle DartUtils::SetStringField(Dart_Handle handle, | |
| const char* name, | |
| const char* val) { | |
| return Dart_SetField(handle, NewString(name), NewString(val)); | |
| } | |
| bool DartUtils::IsDartSchemeURL(const char* url_name) { | |
| static const intptr_t kDartSchemeLen = strlen(kDartScheme); | |
| // If the URL starts with "dart:" then it is considered as a special | |
| // library URL which is handled differently from other URLs. | |
| return (strncmp(url_name, kDartScheme, kDartSchemeLen) == 0); | |
| } | |
| char* DartUtils::DirName(const char* url) { | |
| const char* slash = strrchr(url, File::PathSeparator()[0]); | |
| if (slash == nullptr) { | |
| return Utils::StrDup(url); | |
| } else { | |
| return Utils::StrNDup(url, slash - url + 1); | |
| } | |
| } | |
| void* DartUtils::OpenFile(const char* name, bool write) { | |
| File* file = | |
| File::Open(nullptr, name, write ? File::kWriteTruncate : File::kRead); | |
| return reinterpret_cast<void*>(file); | |
| } | |
| void* DartUtils::OpenFileUri(const char* uri, bool write) { | |
| File* file = | |
| File::OpenUri(nullptr, uri, write ? File::kWriteTruncate : File::kRead); | |
| return reinterpret_cast<void*>(file); | |
| } | |
| void DartUtils::ReadFile(uint8_t** data, intptr_t* len, void* stream) { | |
| ASSERT(data != nullptr); | |
| ASSERT(len != nullptr); | |
| ASSERT(stream != nullptr); | |
| File* file_stream = reinterpret_cast<File*>(stream); | |
| int64_t file_len = file_stream->Length(); | |
| if ((file_len < 0) || (file_len > kIntptrMax)) { | |
| *data = nullptr; | |
| *len = -1; // Indicates read was not successful. | |
| return; | |
| } | |
| *len = static_cast<intptr_t>(file_len); | |
| *data = reinterpret_cast<uint8_t*>(malloc(*len)); | |
| if (!file_stream->ReadFully(*data, *len)) { | |
| free(*data); | |
| *data = nullptr; | |
| *len = -1; // Indicates read was not successful. | |
| return; | |
| } | |
| } | |
| void DartUtils::WriteFile(const void* buffer, | |
| intptr_t num_bytes, | |
| void* stream) { | |
| ASSERT(stream != nullptr); | |
| File* file_stream = reinterpret_cast<File*>(stream); | |
| bool bytes_written = file_stream->WriteFully(buffer, num_bytes); | |
| ASSERT(bytes_written); | |
| } | |
| void DartUtils::CloseFile(void* stream) { | |
| File* file = reinterpret_cast<File*>(stream); | |
| file->Release(); | |
| } | |
| bool DartUtils::EntropySource(uint8_t* buffer, intptr_t length) { | |
| return Crypto::GetRandomBytes(length, buffer); | |
| } | |
| static Dart_Handle SingleArgDart_Invoke(Dart_Handle lib, | |
| const char* method, | |
| Dart_Handle arg) { | |
| const int kNumArgs = 1; | |
| Dart_Handle dart_args[kNumArgs]; | |
| dart_args[0] = arg; | |
| return Dart_Invoke(lib, DartUtils::NewString(method), kNumArgs, dart_args); | |
| } | |
| // TODO(iposva): Allocate from the zone instead of leaking error string | |
| // here. On the other hand the binary is about to exit anyway. | |
| #define SET_ERROR_MSG(error_msg, format, ...) \ | |
| intptr_t len = snprintf(nullptr, 0, format, ##__VA_ARGS__); \ | |
| char* msg = reinterpret_cast<char*>(malloc(len + 1)); \ | |
| snprintf(msg, len + 1, format, ##__VA_ARGS__); \ | |
| *error_msg = msg | |
| static uint8_t* ReadFileFully(const char* filename, | |
| intptr_t* file_len, | |
| const char** error_msg) { | |
| *file_len = -1; | |
| void* stream = DartUtils::OpenFile(filename, false); | |
| if (stream == nullptr) { | |
| SET_ERROR_MSG(error_msg, "Unable to open file: %s", filename); | |
| return nullptr; | |
| } | |
| uint8_t* text_buffer = nullptr; | |
| DartUtils::ReadFile(&text_buffer, file_len, stream); | |
| if (text_buffer == nullptr || *file_len == -1) { | |
| *error_msg = "Unable to read file contents"; | |
| text_buffer = nullptr; | |
| } | |
| DartUtils::CloseFile(stream); | |
| return text_buffer; | |
| } | |
| Dart_Handle DartUtils::ReadStringFromFile(const char* filename) { | |
| const char* error_msg = nullptr; | |
| intptr_t len; | |
| uint8_t* text_buffer = ReadFileFully(filename, &len, &error_msg); | |
| if (text_buffer == nullptr) { | |
| return Dart_NewApiError(error_msg); | |
| } | |
| Dart_Handle str = Dart_NewStringFromUTF8(text_buffer, len); | |
| free(text_buffer); | |
| return str; | |
| } | |
| Dart_Handle DartUtils::MakeUint8Array(const void* buffer, intptr_t len) { | |
| Dart_Handle array = Dart_NewTypedData(Dart_TypedData_kUint8, len); | |
| RETURN_IF_ERROR(array); | |
| { | |
| Dart_TypedData_Type td_type; | |
| void* td_data; | |
| intptr_t td_len; | |
| Dart_Handle result = | |
| Dart_TypedDataAcquireData(array, &td_type, &td_data, &td_len); | |
| RETURN_IF_ERROR(result); | |
| ASSERT(td_type == Dart_TypedData_kUint8); | |
| ASSERT(td_len == len); | |
| ASSERT(td_data != nullptr); | |
| memmove(td_data, buffer, td_len); | |
| result = Dart_TypedDataReleaseData(array); | |
| RETURN_IF_ERROR(result); | |
| } | |
| return array; | |
| } | |
| Dart_Handle DartUtils::SetWorkingDirectory() { | |
| Dart_Handle directory = NewString(original_working_directory); | |
| return SingleArgDart_Invoke(LookupBuiltinLib(), "_setWorkingDirectory", | |
| directory); | |
| } | |
| Dart_Handle DartUtils::ResolveScript(Dart_Handle url) { | |
| const int kNumArgs = 1; | |
| Dart_Handle dart_args[kNumArgs]; | |
| dart_args[0] = url; | |
| return Dart_Invoke(DartUtils::LookupBuiltinLib(), | |
| NewString("_resolveScriptUri"), kNumArgs, dart_args); | |
| } | |
| static bool CheckMagicNumber(const uint8_t* buffer, | |
| intptr_t buffer_length, | |
| const MagicNumberData& magic_number) { | |
| if ((buffer_length >= magic_number.length)) { | |
| return memcmp(buffer, magic_number.bytes, magic_number.length) == 0; | |
| } | |
| return false; | |
| } | |
| DartUtils::MagicNumber DartUtils::SniffForMagicNumber(const char* filename) { | |
| MagicNumber magic_number = DartUtils::kUnknownMagicNumber; | |
| ASSERT(kMaxMagicNumberSize == appjit_magic_number.length); | |
| ASSERT(aotelf_magic_number.length <= appjit_magic_number.length); | |
| ASSERT(static_cast<intptr_t>(sizeof(mach_o::mach_header::magic)) <= | |
| appjit_magic_number.length); | |
| ASSERT(aotpe_magic_number.length <= appjit_magic_number.length); | |
| ASSERT(aotcoff_arm32_magic_number.length <= appjit_magic_number.length); | |
| ASSERT(aotcoff_arm64_magic_number.length <= appjit_magic_number.length); | |
| ASSERT(aotcoff_riscv32_magic_number.length <= appjit_magic_number.length); | |
| ASSERT(aotcoff_riscv64_magic_number.length <= appjit_magic_number.length); | |
| ASSERT(kernel_magic_number.length <= appjit_magic_number.length); | |
| ASSERT(kernel_list_magic_number.length <= appjit_magic_number.length); | |
| ASSERT(bytecode_magic_number.length <= appjit_magic_number.length); | |
| ASSERT(gzip_magic_number.length <= appjit_magic_number.length); | |
| if (File::GetType(nullptr, filename, true) == File::kIsFile) { | |
| File* file = File::Open(nullptr, filename, File::kRead); | |
| if (file != nullptr) { | |
| RefCntReleaseScope<File> rs(file); | |
| uint8_t header[kMaxMagicNumberSize]; | |
| if (file->ReadFully(&header, kMaxMagicNumberSize)) { | |
| magic_number = DartUtils::SniffForMagicNumber(header, sizeof(header)); | |
| } | |
| } | |
| } | |
| return magic_number; | |
| } | |
| DartUtils::MagicNumber DartUtils::SniffForMagicNumber(const uint8_t* buffer, | |
| intptr_t buffer_length) { | |
| if (CheckMagicNumber(buffer, buffer_length, appjit_magic_number)) { | |
| return kAppJITMagicNumber; | |
| } | |
| if (CheckMagicNumber(buffer, buffer_length, kernel_magic_number)) { | |
| return kKernelMagicNumber; | |
| } | |
| if (CheckMagicNumber(buffer, buffer_length, kernel_list_magic_number)) { | |
| return kKernelListMagicNumber; | |
| } | |
| if (CheckMagicNumber(buffer, buffer_length, bytecode_magic_number)) { | |
| return kBytecodeMagicNumber; | |
| } | |
| if (CheckMagicNumber(buffer, buffer_length, gzip_magic_number)) { | |
| return kGzipMagicNumber; | |
| } | |
| if (CheckMagicNumber(buffer, buffer_length, aotelf_magic_number)) { | |
| return kAotELFMagicNumber; | |
| } | |
| // Mach-O magic numbers are reported by whether the endianness of the file | |
| // matches the endianness of the system. Here, we only bother looking for | |
| // host-endian magic numbers, as our Mach-O parsing code won't handle the | |
| // reverse endian case. | |
| if (static_cast<intptr_t>(sizeof(mach_o::mach_header::magic)) <= | |
| buffer_length) { | |
| const uint32_t magic = | |
| reinterpret_cast<const mach_o::mach_header*>(buffer)->magic; | |
| if (magic == mach_o::MH_MAGIC) { | |
| return kAotMachO32MagicNumber; | |
| } else if (magic == mach_o::MH_MAGIC_64) { | |
| return kAotMachO64MagicNumber; | |
| } | |
| } | |
| if (CheckMagicNumber(buffer, buffer_length, aotpe_magic_number)) { | |
| return kAotPEMagicNumber; | |
| } | |
| if (CheckMagicNumber(buffer, buffer_length, aotcoff_arm32_magic_number)) { | |
| return kAotCoffARM32MagicNumber; | |
| } | |
| if (CheckMagicNumber(buffer, buffer_length, aotcoff_arm64_magic_number)) { | |
| return kAotCoffARM64MagicNumber; | |
| } | |
| if (CheckMagicNumber(buffer, buffer_length, aotcoff_riscv32_magic_number)) { | |
| return kAotCoffRISCV32MagicNumber; | |
| } | |
| if (CheckMagicNumber(buffer, buffer_length, aotcoff_riscv64_magic_number)) { | |
| return kAotCoffRISCV64MagicNumber; | |
| } | |
| return kUnknownMagicNumber; | |
| } | |
| Dart_Handle DartUtils::PrepareBuiltinLibrary(Dart_Handle builtin_lib, | |
| Dart_Handle internal_lib, | |
| bool is_service_isolate, | |
| bool trace_loading) { | |
| // Setup the internal library's 'internalPrint' function. | |
| Dart_Handle print = | |
| Dart_Invoke(builtin_lib, NewString("_getPrintClosure"), 0, nullptr); | |
| RETURN_IF_ERROR(print); | |
| Dart_Handle result = | |
| Dart_SetField(internal_lib, NewString("_printClosure"), print); | |
| RETURN_IF_ERROR(result); | |
| if (!is_service_isolate) { | |
| if (IsWindowsHost()) { | |
| result = Dart_SetField(builtin_lib, NewString("_isWindows"), Dart_True()); | |
| RETURN_IF_ERROR(result); | |
| } | |
| if (trace_loading) { | |
| result = | |
| Dart_SetField(builtin_lib, NewString("_traceLoading"), Dart_True()); | |
| RETURN_IF_ERROR(result); | |
| } | |
| // Set current working directory. | |
| result = SetWorkingDirectory(); | |
| RETURN_IF_ERROR(result); | |
| } | |
| return Dart_True(); | |
| } | |
| Dart_Handle DartUtils::PrepareCoreLibrary(Dart_Handle core_lib, | |
| Dart_Handle io_lib) { | |
| // Setup the 'Uri.base' getter in dart:core. | |
| Dart_Handle uri_base = | |
| Dart_Invoke(io_lib, NewString("_getUriBaseClosure"), 0, nullptr); | |
| RETURN_IF_ERROR(uri_base); | |
| Dart_Handle result = | |
| Dart_SetField(core_lib, NewString("_uriBaseClosure"), uri_base); | |
| RETURN_IF_ERROR(result); | |
| return Dart_True(); | |
| } | |
| Dart_Handle DartUtils::PrepareAsyncLibrary(Dart_Handle async_lib, | |
| Dart_Handle isolate_lib, | |
| bool flag_profile_microtasks) { | |
| #if !defined(PRODUCT) | |
| if (flag_profile_microtasks) { | |
| Dart_Handle microtask_mirror_queue_type_name = | |
| Dart_NewStringFromCString("_MicrotaskMirrorQueue"); | |
| RETURN_IF_ERROR(microtask_mirror_queue_type_name); | |
| Dart_Handle microtask_mirror_queue_type = | |
| Dart_GetNonNullableType(async_lib, microtask_mirror_queue_type_name, | |
| /*number_of_type_arguments=*/0, | |
| /*type_arguments=*/nullptr); | |
| RETURN_IF_ERROR(microtask_mirror_queue_type); | |
| Dart_Handle should_profile_microtasks_field_name = | |
| Dart_NewStringFromCString("_shouldProfileMicrotasks"); | |
| RETURN_IF_ERROR(should_profile_microtasks_field_name); | |
| Dart_Handle set_field_result = | |
| Dart_SetField(microtask_mirror_queue_type, | |
| should_profile_microtasks_field_name, Dart_True()); | |
| RETURN_IF_ERROR(set_field_result); | |
| } | |
| #endif // !defined(PRODUCT) | |
| Dart_Handle schedule_immediate_closure = | |
| Dart_Invoke(isolate_lib, NewString("_getIsolateScheduleImmediateClosure"), | |
| 0, nullptr); | |
| RETURN_IF_ERROR(schedule_immediate_closure); | |
| Dart_Handle args[1]; | |
| args[0] = schedule_immediate_closure; | |
| return Dart_Invoke(async_lib, NewString("_setScheduleImmediateClosure"), 1, | |
| args); | |
| } | |
| Dart_Handle DartUtils::PrepareIsolateLibrary(Dart_Handle isolate_lib) { | |
| return Dart_Invoke(isolate_lib, NewString("_setupHooks"), 0, nullptr); | |
| } | |
| Dart_Handle DartUtils::PrepareCLILibrary(Dart_Handle cli_lib) { | |
| return Dart_Null(); | |
| } | |
| Dart_Handle DartUtils::SetupPackageConfig(const char* packages_config) { | |
| Dart_Handle result = Dart_Null(); | |
| if (packages_config != nullptr) { | |
| result = NewString(packages_config); | |
| RETURN_IF_ERROR(result); | |
| const int kNumArgs = 1; | |
| Dart_Handle dart_args[kNumArgs]; | |
| dart_args[0] = result; | |
| result = Dart_Invoke(DartUtils::LookupBuiltinLib(), | |
| NewString("_setPackagesMap"), kNumArgs, dart_args); | |
| } | |
| return result; | |
| } | |
| Dart_Handle DartUtils::SetupCoreLibraries( | |
| bool is_service_isolate, | |
| bool trace_loading, | |
| bool flag_profile_microtasks, | |
| const DartIoSettings& dart_io_settings) { | |
| // First ensure all required libraries are available. | |
| ASSIGN_OR_RETURN(Dart_Handle core_lib, | |
| Dart_LookupLibrary(NewString(kCoreLibURL))); | |
| ASSIGN_OR_RETURN(Dart_Handle async_lib, | |
| Dart_LookupLibrary(NewString(kAsyncLibURL))); | |
| ASSIGN_OR_RETURN(Dart_Handle isolate_lib, | |
| Dart_LookupLibrary(NewString(kIsolateLibURL))); | |
| ASSIGN_OR_RETURN(Dart_Handle internal_lib, | |
| Dart_LookupLibrary(NewString(kInternalLibURL))); | |
| ASSIGN_OR_RETURN(Dart_Handle builtin_lib, | |
| Dart_LookupLibrary(NewString(kBuiltinLibURL))); | |
| ASSIGN_OR_RETURN(Dart_Handle io_lib, | |
| Dart_LookupLibrary(NewString(kIOLibURL))); | |
| ASSIGN_OR_RETURN(Dart_Handle cli_lib, | |
| Dart_LookupLibrary(NewString(kCLILibURL))); | |
| // Configure native resolvers for dart:_builtin and dart:cli. | |
| for (auto id : {Builtin::kBuiltinLibrary, Builtin::kCLILibrary}) { | |
| RETURN_IF_ERROR(Builtin::TrySetNativeResolver(id)); | |
| } | |
| // Initialize dart:io library. | |
| RETURN_IF_ERROR(SetupDartIoLibrary(dart_io_settings)); | |
| // We need to ensure that all the scripts loaded so far are finalized | |
| // as we are about to invoke some Dart code below to setup closures. | |
| Dart_Handle result = Dart_FinalizeLoading(false); | |
| RETURN_IF_ERROR(result); | |
| result = PrepareBuiltinLibrary(builtin_lib, internal_lib, is_service_isolate, | |
| trace_loading); | |
| RETURN_IF_ERROR(result); | |
| RETURN_IF_ERROR( | |
| PrepareAsyncLibrary(async_lib, isolate_lib, flag_profile_microtasks)); | |
| RETURN_IF_ERROR(PrepareCoreLibrary(core_lib, io_lib)); | |
| RETURN_IF_ERROR(PrepareIsolateLibrary(isolate_lib)); | |
| RETURN_IF_ERROR(PrepareCLILibrary(cli_lib)); | |
| return result; | |
| } | |
| bool DartUtils::PostNull(Dart_Port port_id) { | |
| // Post a message with just the null object. | |
| return Dart_PostCObject(port_id, CObject::Null()->AsApiCObject()); | |
| } | |
| bool DartUtils::PostInt32(Dart_Port port_id, int32_t value) { | |
| // Post a message with the integer value. | |
| int32_t min = 0xc0000000; // -1073741824 | |
| int32_t max = 0x3fffffff; // 1073741823 | |
| ASSERT(min <= value && value < max); | |
| Dart_CObject object; | |
| object.type = Dart_CObject_kInt32; | |
| object.value.as_int32 = value; | |
| return Dart_PostCObject(port_id, &object); | |
| } | |
| bool DartUtils::PostInt64(Dart_Port port_id, int64_t value) { | |
| // Post a message with the integer value. | |
| Dart_CObject object; | |
| object.type = Dart_CObject_kInt64; | |
| object.value.as_int64 = value; | |
| return Dart_PostCObject(port_id, &object); | |
| } | |
| bool DartUtils::PostString(Dart_Port port_id, const char* value) { | |
| Dart_CObject* object = CObject::NewString(value); | |
| return Dart_PostCObject(port_id, object); | |
| } | |
| Dart_Handle DartUtils::GetDartType(const char* library_url, | |
| const char* class_name) { | |
| return Dart_GetNonNullableType(Dart_LookupLibrary(NewString(library_url)), | |
| NewString(class_name), 0, nullptr); | |
| } | |
| Dart_Handle DartUtils::NewDartOSError() { | |
| // Extract the current OS error. | |
| OSError os_error; | |
| return NewDartOSError(&os_error); | |
| } | |
| Dart_Handle DartUtils::NewDartOSError(OSError* os_error) { | |
| // Create a dart:io OSError object with the information retrieved from the OS. | |
| Dart_Handle type = GetDartType(kIOLibURL, "OSError"); | |
| ASSERT(!Dart_IsError(type)); | |
| Dart_Handle args[2]; | |
| args[0] = NewString(os_error->message()); | |
| args[1] = Dart_NewInteger(os_error->code()); | |
| return Dart_New(type, Dart_Null(), 2, args); | |
| } | |
| Dart_Handle DartUtils::NewDartExceptionWithOSError(const char* library_url, | |
| const char* exception_name, | |
| const char* message, | |
| Dart_Handle os_error) { | |
| // Create a Dart Exception object with a message and an OSError. | |
| Dart_Handle type = GetDartType(library_url, exception_name); | |
| ASSERT(!Dart_IsError(type)); | |
| Dart_Handle args[2]; | |
| args[0] = NewString(message); | |
| args[1] = os_error; | |
| return Dart_New(type, Dart_Null(), 2, args); | |
| } | |
| Dart_Handle DartUtils::NewDartExceptionWithMessage(const char* library_url, | |
| const char* exception_name, | |
| const char* message) { | |
| // Create a Dart Exception object with a message. | |
| Dart_Handle type = GetDartType(library_url, exception_name); | |
| ASSERT(!Dart_IsError(type)); | |
| if (message != nullptr) { | |
| Dart_Handle args[1]; | |
| args[0] = NewString(message); | |
| return Dart_New(type, Dart_Null(), 1, args); | |
| } else { | |
| return Dart_New(type, Dart_Null(), 0, nullptr); | |
| } | |
| } | |
| Dart_Handle DartUtils::NewDartArgumentError(const char* message) { | |
| return NewDartExceptionWithMessage(kCoreLibURL, "ArgumentError", message); | |
| } | |
| Dart_Handle DartUtils::NewDartFormatException(const char* message) { | |
| return NewDartExceptionWithMessage(kCoreLibURL, "FormatException", message); | |
| } | |
| Dart_Handle DartUtils::NewDartUnsupportedError(const char* message) { | |
| return NewDartExceptionWithMessage(kCoreLibURL, "UnsupportedError", message); | |
| } | |
| Dart_Handle DartUtils::NewDartIOException(const char* exception_name, | |
| const char* message, | |
| Dart_Handle os_error) { | |
| // Create a dart:io exception object of the given type. | |
| return NewDartExceptionWithOSError(kIOLibURL, exception_name, message, | |
| os_error); | |
| } | |
| Dart_Handle DartUtils::NewError(const char* format, ...) { | |
| va_list measure_args; | |
| va_start(measure_args, format); | |
| intptr_t len = Utils::VSNPrint(nullptr, 0, format, measure_args); | |
| va_end(measure_args); | |
| char* buffer = reinterpret_cast<char*>(Dart_ScopeAllocate(len + 1)); | |
| va_list print_args; | |
| va_start(print_args, format); | |
| Utils::VSNPrint(buffer, (len + 1), format, print_args); | |
| va_end(print_args); | |
| return Dart_NewApiError(buffer); | |
| } | |
| Dart_Handle DartUtils::NewInternalError(const char* message) { | |
| return NewDartExceptionWithMessage(kCoreLibURL, "_InternalError", message); | |
| } | |
| Dart_Handle DartUtils::NewStringFormatted(const char* format, ...) { | |
| va_list args; | |
| va_start(args, format); | |
| char* result = ScopedCStringVFormatted(format, args); | |
| va_end(args); | |
| return NewString(result); | |
| } | |
| char* DartUtils::ScopedCStringVFormatted(const char* format, va_list args) { | |
| va_list measure_args; | |
| va_copy(measure_args, args); | |
| intptr_t len = Utils::VSNPrint(nullptr, 0, format, measure_args); | |
| if (len < 0) { | |
| return nullptr; | |
| } | |
| va_end(measure_args); | |
| char* buffer = ScopedCString(len + 1); | |
| va_list print_args; | |
| va_copy(print_args, args); | |
| len = Utils::VSNPrint(buffer, (len + 1), format, print_args); | |
| if (len < 0) { | |
| return nullptr; | |
| } | |
| va_end(print_args); | |
| return buffer; | |
| } | |
| char* DartUtils::ScopedCStringFormatted(const char* format, ...) { | |
| va_list args; | |
| va_start(args, format); | |
| char* result = ScopedCStringVFormatted(format, args); | |
| va_end(args); | |
| return result; | |
| } | |
| bool DartUtils::SetOriginalWorkingDirectory() { | |
| // If we happen to re-initialize the Dart VM multiple times, make sure to free | |
| // the old string (allocated by getcwd()) before setting a new one. | |
| if (original_working_directory != nullptr) { | |
| free(const_cast<char*>(original_working_directory)); | |
| } | |
| original_working_directory = Directory::CurrentNoScope(); | |
| return original_working_directory != nullptr; | |
| } | |
| void DartUtils::SetEnvironment(dart::SimpleHashMap* environment) { | |
| environment_ = environment; | |
| } | |
| Dart_Handle DartUtils::EnvironmentCallback(Dart_Handle name) { | |
| uint8_t* utf8_array; | |
| intptr_t utf8_len; | |
| Dart_Handle result = Dart_Null(); | |
| Dart_Handle handle = Dart_StringToUTF8(name, &utf8_array, &utf8_len); | |
| if (Dart_IsError(handle)) { | |
| handle = Dart_ThrowException( | |
| DartUtils::NewDartArgumentError(Dart_GetError(handle))); | |
| } else { | |
| char* name_chars = reinterpret_cast<char*>(malloc(utf8_len + 1)); | |
| memmove(name_chars, utf8_array, utf8_len); | |
| name_chars[utf8_len] = '\0'; | |
| const char* value = nullptr; | |
| if (environment_ != nullptr) { | |
| SimpleHashMap::Entry* entry = | |
| environment_->Lookup(GetHashmapKeyFromString(name_chars), | |
| SimpleHashMap::StringHash(name_chars), false); | |
| if (entry != nullptr) { | |
| value = reinterpret_cast<char*>(entry->value); | |
| } | |
| } | |
| if (value != nullptr) { | |
| result = Dart_NewStringFromUTF8(reinterpret_cast<const uint8_t*>(value), | |
| strlen(value)); | |
| if (Dart_IsError(result)) { | |
| result = Dart_Null(); | |
| } | |
| } | |
| free(name_chars); | |
| } | |
| return result; | |
| } | |
| // Statically allocated Dart_CObject instances for immutable | |
| // objects. As these will be used by different threads the use of | |
| // these depends on the fact that the marking internally in the | |
| // Dart_CObject structure is not marking simple value objects. | |
| Dart_CObject CObject::api_null_ = {Dart_CObject_kNull, {false}}; | |
| Dart_CObject CObject::api_true_ = {Dart_CObject_kBool, {true}}; | |
| Dart_CObject CObject::api_false_ = {Dart_CObject_kBool, {false}}; | |
| CObject CObject::null_(&api_null_); | |
| CObject CObject::true_(&api_true_); | |
| CObject CObject::false_(&api_false_); | |
| CObject* CObject::Null() { | |
| return &null_; | |
| } | |
| CObject* CObject::True() { | |
| return &true_; | |
| } | |
| CObject* CObject::False() { | |
| return &false_; | |
| } | |
| CObject* CObject::Bool(bool value) { | |
| return value ? &true_ : &false_; | |
| } | |
| Dart_CObject* CObject::New(Dart_CObject_Type type, int additional_bytes) { | |
| Dart_CObject* cobject = reinterpret_cast<Dart_CObject*>( | |
| Dart_ScopeAllocate(sizeof(Dart_CObject) + additional_bytes)); | |
| cobject->type = type; | |
| return cobject; | |
| } | |
| Dart_CObject* CObject::NewInt32(int32_t value) { | |
| Dart_CObject* cobject = New(Dart_CObject_kInt32); | |
| cobject->value.as_int32 = value; | |
| return cobject; | |
| } | |
| Dart_CObject* CObject::NewInt64(int64_t value) { | |
| Dart_CObject* cobject = New(Dart_CObject_kInt64); | |
| cobject->value.as_int64 = value; | |
| return cobject; | |
| } | |
| Dart_CObject* CObject::NewIntptr(intptr_t value) { | |
| // Pointer values passed as intptr_t are always send as int64_t. | |
| Dart_CObject* cobject = New(Dart_CObject_kInt64); | |
| cobject->value.as_int64 = value; | |
| return cobject; | |
| } | |
| Dart_CObject* CObject::NewDouble(double value) { | |
| Dart_CObject* cobject = New(Dart_CObject_kDouble); | |
| cobject->value.as_double = value; | |
| return cobject; | |
| } | |
| Dart_CObject* CObject::NewString(const char* str) { | |
| intptr_t length = strlen(str); | |
| Dart_CObject* cobject = New(Dart_CObject_kString, length + 1); | |
| char* payload = reinterpret_cast<char*>(cobject + 1); | |
| memmove(payload, str, length + 1); | |
| cobject->value.as_string = payload; | |
| return cobject; | |
| } | |
| Dart_CObject* CObject::NewArray(intptr_t length) { | |
| Dart_CObject* cobject = | |
| New(Dart_CObject_kArray, length * sizeof(Dart_CObject*)); // NOLINT | |
| cobject->value.as_array.length = length; | |
| cobject->value.as_array.values = | |
| reinterpret_cast<Dart_CObject**>(cobject + 1); | |
| return cobject; | |
| } | |
| Dart_CObject* CObject::NewUint8Array(const void* data, intptr_t length) { | |
| Dart_CObject* cobject = New(Dart_CObject_kTypedData, length); | |
| memmove(reinterpret_cast<uint8_t*>(cobject + 1), data, length); | |
| cobject->value.as_typed_data.type = Dart_TypedData_kUint8; | |
| cobject->value.as_typed_data.length = length; | |
| cobject->value.as_typed_data.values = | |
| reinterpret_cast<const uint8_t*>(cobject + 1); | |
| return cobject; | |
| } | |
| Dart_CObject* CObject::NewExternalUint8Array(intptr_t length, | |
| uint8_t* data, | |
| void* peer, | |
| Dart_HandleFinalizer callback) { | |
| Dart_CObject* cobject = New(Dart_CObject_kExternalTypedData); | |
| cobject->value.as_external_typed_data.type = Dart_TypedData_kUint8; | |
| cobject->value.as_external_typed_data.length = length; | |
| cobject->value.as_external_typed_data.data = data; | |
| cobject->value.as_external_typed_data.peer = peer; | |
| cobject->value.as_external_typed_data.callback = callback; | |
| return cobject; | |
| } | |
| Dart_CObject* CObject::NewNativePointer(intptr_t ptr, | |
| intptr_t size, | |
| Dart_HandleFinalizer callback) { | |
| Dart_CObject* cobject = New(Dart_CObject_kNativePointer); | |
| cobject->value.as_native_pointer.ptr = ptr; | |
| cobject->value.as_native_pointer.size = size; | |
| cobject->value.as_native_pointer.callback = callback; | |
| return cobject; | |
| } | |
| Dart_CObject* CObject::NewIOBuffer(int64_t length) { | |
| // Make sure that we do not have an integer overflow here. Actual check | |
| // against max elements will be done at the time of writing, as the constant | |
| // is not part of the public API. | |
| if ((length < 0) || (length > kIntptrMax)) { | |
| return nullptr; | |
| } | |
| uint8_t* data = IOBuffer::Allocate(static_cast<intptr_t>(length)); | |
| if (data == nullptr) { | |
| return nullptr; | |
| } | |
| return NewExternalUint8Array(static_cast<intptr_t>(length), data, data, | |
| IOBuffer::Finalizer); | |
| } | |
| void CObject::ShrinkIOBuffer(Dart_CObject* cobject, int64_t new_length) { | |
| if (cobject == nullptr) return; | |
| ASSERT(cobject->type == Dart_CObject_kExternalTypedData); | |
| const auto old_data = cobject->value.as_external_typed_data.data; | |
| const auto old_length = cobject->value.as_external_typed_data.length; | |
| // We only shrink IOBuffers, never grow them. | |
| ASSERT(0 <= new_length && new_length <= old_length); | |
| // We only reallocate if we think the freed space is worth reallocating. | |
| // We consider it worthwhile when freed space is >=25% and we have at | |
| // least 100 free bytes. | |
| const auto free_memory = old_length - new_length; | |
| if ((old_length >> 2) <= free_memory && 100 <= free_memory) { | |
| const auto new_data = IOBuffer::Reallocate(old_data, new_length); | |
| if (new_data != nullptr) { | |
| cobject->value.as_external_typed_data.data = new_data; | |
| cobject->value.as_external_typed_data.peer = new_data; | |
| } | |
| } | |
| // The typed data object always has to have the shranken length. | |
| cobject->value.as_external_typed_data.length = new_length; | |
| } | |
| void CObject::FreeIOBufferData(Dart_CObject* cobject) { | |
| ASSERT(cobject->type == Dart_CObject_kExternalTypedData); | |
| cobject->value.as_external_typed_data.callback( | |
| nullptr, cobject->value.as_external_typed_data.peer); | |
| cobject->value.as_external_typed_data.data = nullptr; | |
| } | |
| CObject* CObject::IllegalArgumentError() { | |
| CObjectArray* result = new CObjectArray(CObject::NewArray(1)); | |
| result->SetAt(0, new CObjectInt32(CObject::NewInt32(kArgumentError))); | |
| return result; | |
| } | |
| CObject* CObject::FileClosedError() { | |
| CObjectArray* result = new CObjectArray(CObject::NewArray(1)); | |
| result->SetAt(0, new CObjectInt32(CObject::NewInt32(kFileClosedError))); | |
| return result; | |
| } | |
| CObject* CObject::NewOSError() { | |
| OSError os_error; | |
| return NewOSError(&os_error); | |
| } | |
| CObject* CObject::NewOSError(OSError* os_error) { | |
| CObject* error_message = | |
| new CObjectString(CObject::NewString(os_error->message())); | |
| CObjectArray* result = new CObjectArray(CObject::NewArray(3)); | |
| result->SetAt(0, new CObjectInt32(CObject::NewInt32(kOSError))); | |
| result->SetAt(1, new CObjectInt32(CObject::NewInt32(os_error->code()))); | |
| result->SetAt(2, error_message); | |
| return result; | |
| } | |
| } // namespace bin | |
| } // namespace dart |