| // 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 "bin/file.h" |
| |
| #include <stdio.h> |
| |
| #include "bin/builtin.h" |
| #include "bin/dartutils.h" |
| #include "bin/io_buffer.h" |
| #include "bin/namespace.h" |
| #include "bin/utils.h" |
| #include "include/bin/dart_io_api.h" |
| #include "include/dart_api.h" |
| #include "include/dart_tools_api.h" |
| #include "platform/globals.h" |
| |
| namespace dart { |
| namespace bin { |
| |
| static constexpr int kFileNativeFieldIndex = 0; |
| |
| #if !defined(PRODUCT) |
| static bool IsFile(Dart_Handle file_obj) { |
| Dart_Handle file_type = ThrowIfError( |
| DartUtils::GetDartType("dart:io", "_RandomAccessFileOpsImpl")); |
| bool isinstance = false; |
| ThrowIfError(Dart_ObjectIsType(file_obj, file_type, &isinstance)); |
| return isinstance; |
| } |
| #endif |
| |
| // The file pointer has been passed into Dart as an intptr_t and it is safe |
| // to pull it out of Dart as a 64-bit integer, cast it to an intptr_t and |
| // from there to a File pointer. |
| static File* GetFile(Dart_NativeArguments args) { |
| File* file; |
| Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
| DEBUG_ASSERT(IsFile(dart_this)); |
| Dart_Handle result = Dart_GetNativeInstanceField( |
| dart_this, kFileNativeFieldIndex, reinterpret_cast<intptr_t*>(&file)); |
| ASSERT(!Dart_IsError(result)); |
| if (file == nullptr) { |
| Dart_PropagateError(Dart_NewUnhandledExceptionError( |
| DartUtils::NewInternalError("No native peer"))); |
| } |
| return file; |
| } |
| |
| static void SetFile(Dart_Handle dart_this, intptr_t file_pointer) { |
| DEBUG_ASSERT(IsFile(dart_this)); |
| Dart_Handle result = Dart_SetNativeInstanceField( |
| dart_this, kFileNativeFieldIndex, file_pointer); |
| ThrowIfError(result); |
| } |
| |
| void FUNCTION_NAME(File_GetPointer)(Dart_NativeArguments args) { |
| File* file = GetFile(args); |
| // If the file is already closed, GetFile() will return nullptr. |
| if (file != nullptr) { |
| // Increment file's reference count. File_GetPointer() should only be called |
| // when we are about to send the File* to the IO Service. |
| file->Retain(); |
| } |
| intptr_t file_pointer = reinterpret_cast<intptr_t>(file); |
| Dart_SetIntegerReturnValue(args, file_pointer); |
| } |
| |
| void FUNCTION_NAME(File_GetFD)(Dart_NativeArguments args) { |
| Dart_SetIntegerReturnValue(args, GetFile(args)->GetFD()); |
| } |
| |
| static void ReleaseFile(void* isolate_callback_data, void* peer) { |
| File* file = reinterpret_cast<File*>(peer); |
| file->Release(); |
| } |
| |
| void FUNCTION_NAME(File_SetPointer)(Dart_NativeArguments args) { |
| Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
| intptr_t file_pointer = DartUtils::GetNativeIntptrArgument(args, 1); |
| File* file = reinterpret_cast<File*>(file_pointer); |
| Dart_FinalizableHandle handle = Dart_NewFinalizableHandle( |
| dart_this, reinterpret_cast<void*>(file), sizeof(*file), ReleaseFile); |
| file->SetFinalizableHandle(handle); |
| SetFile(dart_this, file_pointer); |
| } |
| |
| void FUNCTION_NAME(File_Open)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* filename = DartUtils::GetNativeTypedDataArgument(args, 1); |
| int64_t mode = DartUtils::GetNativeIntegerArgument(args, 2); |
| File::DartFileOpenMode dart_file_mode = |
| static_cast<File::DartFileOpenMode>(mode); |
| File::FileOpenMode file_mode = File::DartModeToFileMode(dart_file_mode); |
| // Check that the file exists before opening it only for |
| // reading. This is to prevent the opening of directories as |
| // files. Directories can be opened for reading using the posix |
| // 'open' call. |
| File* file = File::Open(namespc, filename, file_mode); |
| if (file != nullptr) { |
| Dart_SetIntegerReturnValue(args, reinterpret_cast<intptr_t>(file)); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_Exists)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* filename = DartUtils::GetNativeTypedDataArgument(args, 1); |
| bool exists = File::Exists(namespc, filename); |
| Dart_SetBooleanReturnValue(args, exists); |
| } |
| |
| void FUNCTION_NAME(File_Close)(Dart_NativeArguments args) { |
| // TODO(zra): The bots are hitting a crash in this function, so we include |
| // some checks here that are normally only in a Debug build. When the crash |
| // is gone, this can go back to using GetFile and SetFile. |
| Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0)); |
| #if !defined(PRODUCT) |
| if (!IsFile(dart_this)) { |
| Dart_PropagateError(DartUtils::NewInternalError( |
| "File_Close expects the receiver to be a _RandomAccessFileOpsImpl.")); |
| } |
| #endif |
| File* file; |
| ThrowIfError(Dart_GetNativeInstanceField(dart_this, kFileNativeFieldIndex, |
| reinterpret_cast<intptr_t*>(&file))); |
| if (file == nullptr) { |
| Dart_SetIntegerReturnValue(args, -1); |
| return; |
| } |
| file->Close(); |
| file->DeleteFinalizableHandle(Dart_CurrentIsolate(), dart_this); |
| file->Release(); |
| |
| ThrowIfError( |
| Dart_SetNativeInstanceField(dart_this, kFileNativeFieldIndex, 0)); |
| Dart_SetIntegerReturnValue(args, 0); |
| } |
| |
| void FUNCTION_NAME(File_ReadByte)(Dart_NativeArguments args) { |
| File* file = GetFile(args); |
| ASSERT(file != nullptr); |
| uint8_t buffer; |
| int64_t bytes_read = file->Read(reinterpret_cast<void*>(&buffer), 1); |
| if (bytes_read == 1) { |
| Dart_SetIntegerReturnValue(args, buffer); |
| } else if (bytes_read == 0) { |
| Dart_SetIntegerReturnValue(args, -1); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_WriteByte)(Dart_NativeArguments args) { |
| File* file = GetFile(args); |
| ASSERT(file != nullptr); |
| int64_t byte = 0; |
| if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &byte)) { |
| uint8_t buffer = static_cast<uint8_t>(byte & 0xff); |
| bool success = file->WriteFully(reinterpret_cast<void*>(&buffer), 1); |
| if (success) { |
| Dart_SetIntegerReturnValue(args, 1); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } else { |
| OSError os_error(-1, "Invalid argument", OSError::kUnknown); |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
| } |
| } |
| |
| void FUNCTION_NAME(File_Read)(Dart_NativeArguments args) { |
| File* file = GetFile(args); |
| ASSERT(file != nullptr); |
| Dart_Handle length_object = Dart_GetNativeArgument(args, 1); |
| int64_t length = 0; |
| if (!DartUtils::GetInt64Value(length_object, &length) || (length < 0)) { |
| OSError os_error(-1, "Invalid argument", OSError::kUnknown); |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
| return; |
| } |
| uint8_t* buffer = nullptr; |
| Dart_Handle external_array = IOBuffer::Allocate(length, &buffer); |
| if (Dart_IsNull(external_array)) { |
| OSError os_error(-1, "Failed to allocate buffer", OSError::kUnknown); |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
| return; |
| } |
| int64_t bytes_read = file->Read(reinterpret_cast<void*>(buffer), length); |
| if (bytes_read < 0) { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| return; |
| } |
| if (bytes_read < length) { |
| const int kNumArgs = 3; |
| Dart_Handle dart_args[kNumArgs]; |
| dart_args[0] = external_array; |
| dart_args[1] = Dart_NewInteger(0); |
| dart_args[2] = Dart_NewInteger(bytes_read); |
| // TODO(sgjesse): Cache the _makeUint8ListView function somewhere. |
| Dart_Handle io_lib = Dart_LookupLibrary(DartUtils::NewString("dart:io")); |
| ThrowIfError(io_lib); |
| Dart_Handle array_view = |
| Dart_Invoke(io_lib, DartUtils::NewString("_makeUint8ListView"), |
| kNumArgs, dart_args); |
| Dart_SetReturnValue(args, array_view); |
| } else { |
| Dart_SetReturnValue(args, external_array); |
| } |
| } |
| |
| void FUNCTION_NAME(File_ReadInto)(Dart_NativeArguments args) { |
| File* file = GetFile(args); |
| ASSERT(file != nullptr); |
| Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1); |
| ASSERT(Dart_IsList(buffer_obj)); |
| // start and end arguments are checked in Dart code to be |
| // integers and have the property that end <= |
| // list.length. Therefore, it is safe to extract their value as |
| // intptr_t. |
| intptr_t start = DartUtils::GetNativeIntptrArgument(args, 2); |
| intptr_t end = DartUtils::GetNativeIntptrArgument(args, 3); |
| intptr_t length = end - start; |
| intptr_t array_len = 0; |
| Dart_Handle result = Dart_ListLength(buffer_obj, &array_len); |
| ThrowIfError(result); |
| ASSERT(end <= array_len); |
| |
| uint8_t* buffer; |
| bool is_byte_data = false; |
| |
| if (Dart_IsTypedData(buffer_obj)) { |
| // Avoid a memory copy if the input List<int> is an UInt8List. |
| Dart_TypedData_Type data_type; |
| intptr_t bytes_count; |
| ThrowIfError(Dart_TypedDataAcquireData(buffer_obj, &data_type, |
| reinterpret_cast<void**>(&buffer), |
| &bytes_count)); |
| if (data_type == Dart_TypedData_kUint8) { |
| is_byte_data = true; |
| buffer += start; |
| ASSERT(bytes_count == array_len); |
| } else { |
| ThrowIfError(Dart_TypedDataReleaseData(buffer_obj)); |
| } |
| } |
| |
| if (!is_byte_data) { |
| buffer = Dart_ScopeAllocate(length); |
| } |
| |
| int64_t bytes_read = file->Read(reinterpret_cast<void*>(buffer), length); |
| OSError* os_error = new OSError(); // capture error if any |
| if (is_byte_data) { |
| Dart_Handle handle = Dart_TypedDataReleaseData(buffer_obj); |
| if (Dart_IsError(handle)) { |
| delete os_error; |
| Dart_PropagateError(handle); |
| } |
| } |
| if (bytes_read >= 0) { |
| if (!is_byte_data) { |
| Dart_Handle handle = |
| Dart_ListSetAsBytes(buffer_obj, start, buffer, bytes_read); |
| if (Dart_IsError(handle)) { |
| delete os_error; |
| Dart_PropagateError(handle); |
| } |
| } |
| Dart_SetIntegerReturnValue(args, bytes_read); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError(os_error)); |
| } |
| delete os_error; |
| } |
| |
| void FUNCTION_NAME(File_WriteFrom)(Dart_NativeArguments args) { |
| File* file = GetFile(args); |
| ASSERT(file != nullptr); |
| |
| Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1); |
| |
| // Offset and length arguments are checked in Dart code to be |
| // integers and have the property that (offset + length) <= |
| // list.length. Therefore, it is safe to extract their value as |
| // intptr_t. |
| intptr_t start = DartUtils::GetNativeIntptrArgument(args, 2); |
| intptr_t end = DartUtils::GetNativeIntptrArgument(args, 3); |
| |
| // The buffer object passed in has to be an Int8List or Uint8List object. |
| // Acquire a direct pointer to the data area of the buffer object. |
| Dart_TypedData_Type type; |
| intptr_t length = end - start; |
| intptr_t buffer_len = 0; |
| void* buffer = nullptr; |
| Dart_Handle result = |
| Dart_TypedDataAcquireData(buffer_obj, &type, &buffer, &buffer_len); |
| ThrowIfError(result); |
| |
| ASSERT(type == Dart_TypedData_kUint8 || type == Dart_TypedData_kInt8); |
| ASSERT(end <= buffer_len); |
| ASSERT(buffer != nullptr); |
| |
| // Write all the data out into the file. |
| char* byte_buffer = reinterpret_cast<char*>(buffer); |
| bool success = file->WriteFully(byte_buffer + start, length); |
| OSError* os_error = new OSError(); // capture error if any |
| |
| // Release the direct pointer acquired above. |
| result = Dart_TypedDataReleaseData(buffer_obj); |
| if (Dart_IsError(result)) { |
| delete os_error; |
| Dart_PropagateError(result); |
| } |
| if (!success) { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError(os_error)); |
| } else { |
| Dart_SetReturnValue(args, Dart_Null()); |
| } |
| delete os_error; |
| } |
| |
| void FUNCTION_NAME(File_Position)(Dart_NativeArguments args) { |
| File* file = GetFile(args); |
| ASSERT(file != nullptr); |
| intptr_t return_value = file->Position(); |
| if (return_value >= 0) { |
| Dart_SetIntegerReturnValue(args, return_value); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_SetPosition)(Dart_NativeArguments args) { |
| File* file = GetFile(args); |
| ASSERT(file != nullptr); |
| int64_t position = 0; |
| if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &position)) { |
| if (file->SetPosition(position)) { |
| Dart_SetBooleanReturnValue(args, true); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } else { |
| OSError os_error(-1, "Invalid argument", OSError::kUnknown); |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
| } |
| } |
| |
| void FUNCTION_NAME(File_Truncate)(Dart_NativeArguments args) { |
| File* file = GetFile(args); |
| ASSERT(file != nullptr); |
| int64_t length = 0; |
| if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &length)) { |
| if (file->Truncate(length)) { |
| Dart_SetBooleanReturnValue(args, true); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } else { |
| OSError os_error(-1, "Invalid argument", OSError::kUnknown); |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
| } |
| } |
| |
| void FUNCTION_NAME(File_Length)(Dart_NativeArguments args) { |
| File* file = GetFile(args); |
| ASSERT(file != nullptr); |
| int64_t return_value = file->Length(); |
| if (return_value >= 0) { |
| Dart_SetIntegerReturnValue(args, return_value); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_LengthFromPath)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* path = DartUtils::GetNativeTypedDataArgument(args, 1); |
| int64_t return_value = File::LengthFromPath(namespc, path); |
| if (return_value >= 0) { |
| Dart_SetIntegerReturnValue(args, return_value); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_LastModified)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* raw_name = DartUtils::GetNativeTypedDataArgument(args, 1); |
| int64_t return_value = File::LastModified(namespc, raw_name); |
| if (return_value >= 0) { |
| Dart_SetIntegerReturnValue(args, return_value * kMillisecondsPerSecond); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_SetLastModified)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| int64_t millis; |
| if (!DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 2), &millis)) { |
| Dart_ThrowException(DartUtils::NewDartArgumentError( |
| "The second argument must be a 64-bit int.")); |
| } |
| const char* name = DartUtils::GetNativeTypedDataArgument(args, 1); |
| bool result = File::SetLastModified(namespc, name, millis); |
| if (!result) { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_LastAccessed)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* name = DartUtils::GetNativeTypedDataArgument(args, 1); |
| int64_t return_value = File::LastAccessed(namespc, name); |
| if (return_value >= 0) { |
| Dart_SetIntegerReturnValue(args, return_value * kMillisecondsPerSecond); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_SetLastAccessed)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| int64_t millis; |
| if (!DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 2), &millis)) { |
| Dart_ThrowException(DartUtils::NewDartArgumentError( |
| "The second argument must be a 64-bit int.")); |
| } |
| const char* name = DartUtils::GetNativeTypedDataArgument(args, 1); |
| bool result = File::SetLastAccessed(namespc, name, millis); |
| if (!result) { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_Flush)(Dart_NativeArguments args) { |
| File* file = GetFile(args); |
| ASSERT(file != nullptr); |
| if (file->Flush()) { |
| Dart_SetBooleanReturnValue(args, true); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_Lock)(Dart_NativeArguments args) { |
| File* file = GetFile(args); |
| ASSERT(file != nullptr); |
| int64_t lock; |
| int64_t start; |
| int64_t end; |
| if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &lock) && |
| DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 2), &start) && |
| DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 3), &end)) { |
| if ((lock >= File::kLockMin) && (lock <= File::kLockMax) && (start >= 0) && |
| (end == -1 || end > start)) { |
| if (file->Lock(static_cast<File::LockType>(lock), start, end)) { |
| Dart_SetBooleanReturnValue(args, true); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| return; |
| } |
| } |
| OSError os_error(-1, "Invalid argument", OSError::kUnknown); |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error)); |
| } |
| |
| void FUNCTION_NAME(File_Create)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| Dart_Handle exclusive_handle = Dart_GetNativeArgument(args, 2); |
| const char* path = DartUtils::GetNativeTypedDataArgument(args, 1); |
| bool exclusive = DartUtils::GetBooleanValue(exclusive_handle); |
| bool result = File::Create(namespc, path, exclusive); |
| if (result) { |
| Dart_SetBooleanReturnValue(args, result); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_CreateLink)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* name = DartUtils::GetNativeTypedDataArgument(args, 1); |
| const char* target = DartUtils::GetNativeStringArgument(args, 2); |
| bool result = File::CreateLink(namespc, name, target); |
| if (!result) { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_CreatePipe)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| |
| File* readPipe; |
| File* writePipe; |
| if (File::CreatePipe(namespc, &readPipe, &writePipe)) { |
| Dart_Handle pipes = ThrowIfError(Dart_NewList(2)); |
| Dart_Handle readHandle = |
| ThrowIfError(Dart_NewInteger(reinterpret_cast<intptr_t>(readPipe))); |
| Dart_Handle writeHandle = |
| ThrowIfError(Dart_NewInteger(reinterpret_cast<intptr_t>(writePipe))); |
| ThrowIfError(Dart_ListSetAt(pipes, 0, readHandle)); |
| ThrowIfError(Dart_ListSetAt(pipes, 1, writeHandle)); |
| Dart_SetReturnValue(args, pipes); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_LinkTarget)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* name = DartUtils::GetNativeTypedDataArgument(args, 1); |
| const char* target = File::LinkTarget(namespc, name); |
| if (target == nullptr) { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } else { |
| Dart_Handle str = ThrowIfError(DartUtils::NewString(target)); |
| Dart_SetReturnValue(args, str); |
| } |
| } |
| |
| void FUNCTION_NAME(File_Delete)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* path = DartUtils::GetNativeTypedDataArgument(args, 1); |
| bool result = File::Delete(namespc, path); |
| if (result) { |
| Dart_SetBooleanReturnValue(args, result); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_DeleteLink)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* path = DartUtils::GetNativeTypedDataArgument(args, 1); |
| bool result = File::DeleteLink(namespc, path); |
| if (result) { |
| Dart_SetBooleanReturnValue(args, result); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_Rename)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* old_path = DartUtils::GetNativeTypedDataArgument(args, 1); |
| const char* new_path = DartUtils::GetNativeStringArgument(args, 2); |
| bool result = File::Rename(namespc, old_path, new_path); |
| if (result) { |
| Dart_SetBooleanReturnValue(args, result); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_RenameLink)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* old_path = DartUtils::GetNativeTypedDataArgument(args, 1); |
| const char* new_path = DartUtils::GetNativeStringArgument(args, 2); |
| bool result = File::RenameLink(namespc, old_path, new_path); |
| if (result) { |
| Dart_SetBooleanReturnValue(args, result); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_Copy)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* old_path = DartUtils::GetNativeTypedDataArgument(args, 1); |
| const char* new_path = DartUtils::GetNativeStringArgument(args, 2); |
| bool result = File::Copy(namespc, old_path, new_path); |
| if (result) { |
| Dart_SetBooleanReturnValue(args, result); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_ResolveSymbolicLinks)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* path = nullptr; |
| const char* str = DartUtils::GetNativeTypedDataArgument(args, 1); |
| path = File::GetCanonicalPath(namespc, str); |
| if (path != nullptr) { |
| Dart_Handle str = ThrowIfError(DartUtils::NewString(path)); |
| Dart_SetReturnValue(args, str); |
| } else { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } |
| } |
| |
| void FUNCTION_NAME(File_OpenStdio)(Dart_NativeArguments args) { |
| const int64_t fd = DartUtils::GetNativeIntegerArgument(args, 0); |
| File* file = File::OpenStdio(static_cast<int>(fd)); |
| Dart_SetIntegerReturnValue(args, reinterpret_cast<intptr_t>(file)); |
| } |
| |
| void FUNCTION_NAME(File_GetStdioHandleType)(Dart_NativeArguments args) { |
| int64_t fd = DartUtils::GetNativeIntegerArgument(args, 0); |
| ASSERT((fd == STDIN_FILENO) || (fd == STDOUT_FILENO) || |
| (fd == STDERR_FILENO)); |
| File::StdioHandleType type = File::GetStdioHandleType(static_cast<int>(fd)); |
| if (type == File::StdioHandleType::kTypeError) { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } else { |
| Dart_SetIntegerReturnValue(args, type); |
| } |
| } |
| |
| void FUNCTION_NAME(File_GetType)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* path = DartUtils::GetNativeTypedDataArgument(args, 1); |
| bool follow_links = DartUtils::GetNativeBooleanArgument(args, 2); |
| File::Type type = File::GetType(namespc, path, follow_links); |
| Dart_SetIntegerReturnValue(args, static_cast<int>(type)); |
| } |
| |
| void FUNCTION_NAME(File_Stat)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* path = DartUtils::GetNativeStringArgument(args, 1); |
| |
| int64_t stat_data[File::kStatSize]; |
| File::Stat(namespc, path, stat_data); |
| if (stat_data[File::kType] == File::kDoesNotExist) { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| return; |
| } |
| Dart_Handle returned_data = |
| Dart_NewTypedData(Dart_TypedData_kInt64, File::kStatSize); |
| ThrowIfError(returned_data); |
| Dart_TypedData_Type data_type_unused; |
| void* data_location; |
| intptr_t data_length_unused; |
| Dart_Handle status = Dart_TypedDataAcquireData( |
| returned_data, &data_type_unused, &data_location, &data_length_unused); |
| ThrowIfError(status); |
| memmove(data_location, stat_data, File::kStatSize * sizeof(int64_t)); |
| status = Dart_TypedDataReleaseData(returned_data); |
| ThrowIfError(status); |
| Dart_SetReturnValue(args, returned_data); |
| } |
| |
| void FUNCTION_NAME(File_AreIdentical)(Dart_NativeArguments args) { |
| Namespace* namespc = Namespace::GetNamespace(args, 0); |
| const char* path_1 = DartUtils::GetNativeStringArgument(args, 1); |
| const char* path_2 = DartUtils::GetNativeStringArgument(args, 2); |
| File::Identical result = File::AreIdentical(namespc, path_1, namespc, path_2); |
| if (result == File::kError) { |
| Dart_SetReturnValue(args, DartUtils::NewDartOSError()); |
| } else { |
| Dart_SetBooleanReturnValue(args, result == File::kIdentical); |
| } |
| } |
| |
| #define IS_SEPARATOR(c) ((c) == '/' || (c) == 0) |
| |
| // Checks that if we increment this index forward, we'll still have enough space |
| // for a null terminator within PATH_MAX bytes. |
| #define CHECK_CAN_INCREMENT(i) \ |
| if ((i) + 1 >= outlen) { \ |
| return -1; \ |
| } |
| |
| intptr_t File::CleanUnixPath(const char* in, char* out, intptr_t outlen) { |
| if (in[0] == 0) { |
| snprintf(out, outlen, "."); |
| return 1; |
| } |
| |
| const bool rooted = (in[0] == '/'); |
| intptr_t in_index = 0; // Index of the next byte to read. |
| intptr_t out_index = 0; // Index of the next byte to write. |
| |
| if (rooted) { |
| out[out_index++] = '/'; |
| in_index++; |
| } |
| // The output index at which '..' cannot be cleaned further. |
| intptr_t dotdot = out_index; |
| |
| while (in[in_index] != 0) { |
| if (in[in_index] == '/') { |
| // 1. Reduce multiple slashes to a single slash. |
| CHECK_CAN_INCREMENT(in_index); |
| in_index++; |
| } else if ((in[in_index] == '.') && IS_SEPARATOR(in[in_index + 1])) { |
| // 2. Eliminate . path name elements (the current directory). |
| CHECK_CAN_INCREMENT(in_index); |
| in_index++; |
| } else if ((in[in_index] == '.') && (in[in_index + 1] == '.') && |
| IS_SEPARATOR(in[in_index + 2])) { |
| CHECK_CAN_INCREMENT(in_index + 1); |
| in_index += 2; |
| if (out_index > dotdot) { |
| // 3. Eliminate .. path elements (the parent directory) and the element |
| // that precedes them. |
| out_index--; |
| while ((out_index > dotdot) && (out[out_index] != '/')) { |
| out_index--; |
| } |
| } else if (rooted) { |
| // 4. Eliminate .. elements that begin a rooted path, that is, replace |
| // /.. by / at the beginning of a path. |
| continue; |
| } else if (!rooted) { |
| if (out_index > 0) { |
| out[out_index++] = '/'; |
| } |
| // 5. Leave intact .. elements that begin a non-rooted path. |
| out[out_index++] = '.'; |
| out[out_index++] = '.'; |
| dotdot = out_index; |
| } |
| } else { |
| if ((rooted && out_index != 1) || (!rooted && out_index != 0)) { |
| // Add '/' before normal path component, for non-root components. |
| out[out_index++] = '/'; |
| } |
| |
| while (!IS_SEPARATOR(in[in_index])) { |
| CHECK_CAN_INCREMENT(in_index); |
| out[out_index++] = in[in_index++]; |
| } |
| } |
| } |
| |
| if (out_index == 0) { |
| snprintf(out, outlen, "."); |
| return 1; |
| } |
| |
| // Append null character. |
| out[out_index] = 0; |
| return out_index; |
| } |
| |
| static int64_t CObjectInt32OrInt64ToInt64(CObject* cobject) { |
| ASSERT(cobject->IsInt32OrInt64()); |
| int64_t result; |
| if (cobject->IsInt32()) { |
| CObjectInt32 value(cobject); |
| result = value.Value(); |
| } else { |
| CObjectInt64 value(cobject); |
| result = value.Value(); |
| } |
| return result; |
| } |
| |
| static File* CObjectToFilePointer(CObject* cobject) { |
| CObjectIntptr value(cobject); |
| return reinterpret_cast<File*>(value.Value()); |
| } |
| |
| static Namespace* CObjectToNamespacePointer(CObject* cobject) { |
| CObjectIntptr value(cobject); |
| return reinterpret_cast<Namespace*>(value.Value()); |
| } |
| |
| CObject* File::ExistsRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if ((request.Length() != 2) || !request[1]->IsUint8Array()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array filename(request[1]); |
| return CObject::Bool( |
| File::Exists(namespc, reinterpret_cast<const char*>(filename.Buffer()))); |
| } |
| |
| CObject* File::CreateRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if ((request.Length() != 3) || !request[1]->IsUint8Array() || |
| !request[2]->IsBool()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array filename(request[1]); |
| CObjectBool exclusive(request[2]); |
| return File::Create(namespc, reinterpret_cast<const char*>(filename.Buffer()), |
| exclusive.Value()) |
| ? CObject::True() |
| : CObject::NewOSError(); |
| } |
| |
| CObject* File::CreatePipeRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| |
| File* readPipe; |
| File* writePipe; |
| if (!CreatePipe(namespc, &readPipe, &writePipe)) { |
| return CObject::NewOSError(); |
| } |
| |
| CObjectArray* pipes = new CObjectArray(CObject::NewArray(2)); |
| CObjectNativePointer* readHandle = new CObjectNativePointer( |
| CObject::NewNativePointer(reinterpret_cast<intptr_t>(readPipe), |
| sizeof(*readPipe), ReleaseFile)); |
| CObjectNativePointer* writeHandle = new CObjectNativePointer( |
| CObject::NewNativePointer(reinterpret_cast<intptr_t>(writePipe), |
| sizeof(*writePipe), ReleaseFile)); |
| pipes->SetAt(0, readHandle); |
| pipes->SetAt(1, writeHandle); |
| return pipes; |
| } |
| |
| CObject* File::OpenRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if ((request.Length() != 3) || !request[1]->IsUint8Array() || |
| !request[2]->IsInt32()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array filename(request[1]); |
| CObjectInt32 mode(request[2]); |
| File::DartFileOpenMode dart_file_mode = |
| static_cast<File::DartFileOpenMode>(mode.Value()); |
| File::FileOpenMode file_mode = File::DartModeToFileMode(dart_file_mode); |
| File* file = File::Open( |
| namespc, reinterpret_cast<const char*>(filename.Buffer()), file_mode); |
| if (file == nullptr) { |
| return CObject::NewOSError(); |
| } |
| return new CObjectNativePointer(CObject::NewNativePointer( |
| reinterpret_cast<intptr_t>(file), sizeof(*file), ReleaseFile)); |
| } |
| |
| CObject* File::DeleteRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::False(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if ((request.Length() != 2) || !request[1]->IsUint8Array()) { |
| return CObject::False(); |
| } |
| CObjectUint8Array filename(request[1]); |
| return File::Delete(namespc, reinterpret_cast<const char*>(filename.Buffer())) |
| ? CObject::True() |
| : CObject::NewOSError(); |
| } |
| |
| CObject* File::RenameRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if ((request.Length() != 3) || !request[1]->IsUint8Array() || |
| !request[2]->IsString()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array old_path(request[1]); |
| CObjectString new_path(request[2]); |
| return File::Rename(namespc, reinterpret_cast<const char*>(old_path.Buffer()), |
| new_path.CString()) |
| ? CObject::True() |
| : CObject::NewOSError(); |
| } |
| |
| CObject* File::CopyRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if ((request.Length() != 3) || !request[1]->IsUint8Array() || |
| !request[2]->IsString()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array old_path(request[1]); |
| CObjectString new_path(request[2]); |
| return File::Copy(namespc, reinterpret_cast<const char*>(old_path.Buffer()), |
| new_path.CString()) |
| ? CObject::True() |
| : CObject::NewOSError(); |
| } |
| |
| CObject* File::ResolveSymbolicLinksRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if ((request.Length() != 2) || !request[1]->IsUint8Array()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array filename(request[1]); |
| const char* result = File::GetCanonicalPath( |
| namespc, reinterpret_cast<const char*>(filename.Buffer())); |
| if (result == nullptr) { |
| return CObject::NewOSError(); |
| } |
| return new CObjectString(CObject::NewString(result)); |
| } |
| |
| CObject* File::CloseRequest(const CObjectArray& request) { |
| if ((request.Length() != 1) || !request[0]->IsIntptr()) { |
| return new CObjectIntptr(CObject::NewIntptr(-1)); |
| } |
| File* file = CObjectToFilePointer(request[0]); |
| RefCntReleaseScope<File> rs(file); |
| // We have retained a reference to the file here. Therefore the file's |
| // destructor can't be running. Since no further requests are dispatched by |
| // the Dart code after an async close call, this Close() can't be racing |
| // with any other call on the file. We don't do an extra Release(), and we |
| // don't delete the weak persistent handle. The file is closed here, but the |
| // memory will be cleaned up when the finalizer runs. |
| ASSERT(!file->IsClosed()); |
| file->Close(); |
| return new CObjectIntptr(CObject::NewIntptr(0)); |
| } |
| |
| CObject* File::PositionRequest(const CObjectArray& request) { |
| if ((request.Length() != 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| File* file = CObjectToFilePointer(request[0]); |
| RefCntReleaseScope<File> rs(file); |
| if (file->IsClosed()) { |
| return CObject::FileClosedError(); |
| } |
| const intptr_t return_value = file->Position(); |
| if (return_value < 0) { |
| return CObject::NewOSError(); |
| } |
| return new CObjectIntptr(CObject::NewIntptr(return_value)); |
| } |
| |
| CObject* File::SetPositionRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| File* file = CObjectToFilePointer(request[0]); |
| RefCntReleaseScope<File> rs(file); |
| if ((request.Length() != 2) || !request[1]->IsInt32OrInt64()) { |
| return CObject::IllegalArgumentError(); |
| } |
| if (file->IsClosed()) { |
| return CObject::FileClosedError(); |
| } |
| const int64_t position = CObjectInt32OrInt64ToInt64(request[1]); |
| return file->SetPosition(position) ? CObject::True() : CObject::NewOSError(); |
| } |
| |
| CObject* File::TruncateRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| File* file = CObjectToFilePointer(request[0]); |
| RefCntReleaseScope<File> rs(file); |
| if ((request.Length() != 2) || !request[1]->IsInt32OrInt64()) { |
| return CObject::IllegalArgumentError(); |
| } |
| if (file->IsClosed()) { |
| return CObject::FileClosedError(); |
| } |
| const int64_t length = CObjectInt32OrInt64ToInt64(request[1]); |
| if (file->Truncate(length)) { |
| return CObject::True(); |
| } |
| return CObject::NewOSError(); |
| } |
| |
| CObject* File::LengthRequest(const CObjectArray& request) { |
| if ((request.Length() != 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| File* file = CObjectToFilePointer(request[0]); |
| RefCntReleaseScope<File> rs(file); |
| if (file->IsClosed()) { |
| return CObject::FileClosedError(); |
| } |
| const int64_t return_value = file->Length(); |
| if (return_value < 0) { |
| return CObject::NewOSError(); |
| } |
| return new CObjectInt64(CObject::NewInt64(return_value)); |
| } |
| |
| CObject* File::LengthFromPathRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if ((request.Length() != 2) || !request[1]->IsUint8Array()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array filepath(request[1]); |
| const int64_t return_value = File::LengthFromPath( |
| namespc, reinterpret_cast<const char*>(filepath.Buffer())); |
| if (return_value < 0) { |
| return CObject::NewOSError(); |
| } |
| return new CObjectInt64(CObject::NewInt64(return_value)); |
| } |
| |
| CObject* File::LastAccessedRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if ((request.Length() != 2) || !request[1]->IsUint8Array()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array filepath(request[1]); |
| const int64_t return_value = File::LastAccessed( |
| namespc, reinterpret_cast<const char*>(filepath.Buffer())); |
| if (return_value < 0) { |
| return CObject::NewOSError(); |
| } |
| return new CObjectIntptr( |
| CObject::NewInt64(return_value * kMillisecondsPerSecond)); |
| } |
| |
| CObject* File::SetLastAccessedRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if ((request.Length() != 3) || !request[1]->IsUint8Array() || |
| !request[2]->IsInt32OrInt64()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array filepath(request[1]); |
| const int64_t millis = CObjectInt32OrInt64ToInt64(request[2]); |
| return File::SetLastAccessed( |
| namespc, reinterpret_cast<const char*>(filepath.Buffer()), millis) |
| ? CObject::Null() |
| : CObject::NewOSError(); |
| } |
| |
| CObject* File::LastModifiedRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if ((request.Length() != 2) || !request[1]->IsUint8Array()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array filepath(request[1]); |
| const int64_t return_value = File::LastModified( |
| namespc, reinterpret_cast<const char*>(filepath.Buffer())); |
| if (return_value < 0) { |
| return CObject::NewOSError(); |
| } |
| return new CObjectIntptr( |
| CObject::NewInt64(return_value * kMillisecondsPerSecond)); |
| } |
| |
| CObject* File::SetLastModifiedRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if ((request.Length() != 3) || !request[1]->IsUint8Array() || |
| !request[2]->IsInt32OrInt64()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array filepath(request[1]); |
| const int64_t millis = CObjectInt32OrInt64ToInt64(request[2]); |
| return File::SetLastModified( |
| namespc, reinterpret_cast<const char*>(filepath.Buffer()), millis) |
| ? CObject::Null() |
| : CObject::NewOSError(); |
| } |
| |
| CObject* File::FlushRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| File* file = CObjectToFilePointer(request[0]); |
| RefCntReleaseScope<File> rs(file); |
| if (file->IsClosed()) { |
| return CObject::FileClosedError(); |
| } |
| return file->Flush() ? CObject::True() : CObject::NewOSError(); |
| } |
| |
| CObject* File::ReadByteRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| File* file = CObjectToFilePointer(request[0]); |
| RefCntReleaseScope<File> rs(file); |
| if (file->IsClosed()) { |
| return CObject::FileClosedError(); |
| } |
| uint8_t buffer; |
| const int64_t bytes_read = file->Read(reinterpret_cast<void*>(&buffer), 1); |
| if (bytes_read < 0) { |
| return CObject::NewOSError(); |
| } |
| if (bytes_read == 0) { |
| return new CObjectIntptr(CObject::NewIntptr(-1)); |
| } |
| return new CObjectIntptr(CObject::NewIntptr(buffer)); |
| } |
| |
| CObject* File::WriteByteRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| File* file = CObjectToFilePointer(request[0]); |
| RefCntReleaseScope<File> rs(file); |
| if ((request.Length() != 2) || !request[1]->IsInt32OrInt64()) { |
| return CObject::IllegalArgumentError(); |
| } |
| if (file->IsClosed()) { |
| return CObject::FileClosedError(); |
| } |
| const int64_t byte = CObjectInt32OrInt64ToInt64(request[1]); |
| uint8_t buffer = static_cast<uint8_t>(byte & 0xff); |
| return file->WriteFully(reinterpret_cast<void*>(&buffer), 1) |
| ? new CObjectInt64(CObject::NewInt64(1)) |
| : CObject::NewOSError(); |
| } |
| |
| CObject* File::ReadRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| File* file = CObjectToFilePointer(request[0]); |
| RefCntReleaseScope<File> rs(file); |
| if ((request.Length() != 2) || !request[1]->IsInt32OrInt64()) { |
| return CObject::IllegalArgumentError(); |
| } |
| if (file->IsClosed()) { |
| return CObject::FileClosedError(); |
| } |
| const int64_t length = CObjectInt32OrInt64ToInt64(request[1]); |
| Dart_CObject* io_buffer = CObject::NewIOBuffer(length); |
| if (io_buffer == nullptr) { |
| return CObject::NewOSError(); |
| } |
| uint8_t* data = io_buffer->value.as_external_typed_data.data; |
| const int64_t bytes_read = file->Read(data, length); |
| if (bytes_read < 0) { |
| CObject* error = CObject::NewOSError(); |
| CObject::FreeIOBufferData(io_buffer); |
| return error; |
| } |
| |
| // Possibly shrink the used malloc() storage if the actual number of bytes is |
| // significantly lower. |
| CObject::ShrinkIOBuffer(io_buffer, bytes_read); |
| |
| auto external_array = new CObjectExternalUint8Array(io_buffer); |
| CObjectArray* result = new CObjectArray(CObject::NewArray(2)); |
| result->SetAt(0, new CObjectIntptr(CObject::NewInt32(0))); |
| result->SetAt(1, external_array); |
| return result; |
| } |
| |
| CObject* File::ReadIntoRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| File* file = CObjectToFilePointer(request[0]); |
| RefCntReleaseScope<File> rs(file); |
| if ((request.Length() != 2) || !request[1]->IsInt32OrInt64()) { |
| return CObject::IllegalArgumentError(); |
| } |
| if (file->IsClosed()) { |
| return CObject::FileClosedError(); |
| } |
| const int64_t length = CObjectInt32OrInt64ToInt64(request[1]); |
| Dart_CObject* io_buffer = CObject::NewIOBuffer(length); |
| if (io_buffer == nullptr) { |
| return CObject::NewOSError(); |
| } |
| uint8_t* data = io_buffer->value.as_external_typed_data.data; |
| const int64_t bytes_read = file->Read(data, length); |
| if (bytes_read < 0) { |
| CObject* error = CObject::NewOSError(); |
| CObject::FreeIOBufferData(io_buffer); |
| return error; |
| } |
| |
| // Possibly shrink the used malloc() storage if the actual number of bytes is |
| // significantly lower. |
| CObject::ShrinkIOBuffer(io_buffer, bytes_read); |
| |
| auto external_array = new CObjectExternalUint8Array(io_buffer); |
| CObjectArray* result = new CObjectArray(CObject::NewArray(3)); |
| result->SetAt(0, new CObjectIntptr(CObject::NewInt32(0))); |
| result->SetAt(1, new CObjectInt64(CObject::NewInt64(bytes_read))); |
| result->SetAt(2, external_array); |
| return result; |
| } |
| |
| static int SizeInBytes(Dart_TypedData_Type type) { |
| switch (type) { |
| case Dart_TypedData_kInt8: |
| case Dart_TypedData_kUint8: |
| case Dart_TypedData_kUint8Clamped: |
| return 1; |
| case Dart_TypedData_kInt16: |
| case Dart_TypedData_kUint16: |
| return 2; |
| case Dart_TypedData_kInt32: |
| case Dart_TypedData_kUint32: |
| case Dart_TypedData_kFloat32: |
| return 4; |
| case Dart_TypedData_kInt64: |
| case Dart_TypedData_kUint64: |
| case Dart_TypedData_kFloat64: |
| return 8; |
| case Dart_TypedData_kInt32x4: |
| case Dart_TypedData_kFloat32x4: |
| case Dart_TypedData_kFloat64x2: |
| return 16; |
| default: |
| break; |
| } |
| UNREACHABLE(); |
| return -1; |
| } |
| |
| CObject* File::WriteFromRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| File* file = CObjectToFilePointer(request[0]); |
| RefCntReleaseScope<File> rs(file); |
| if ((request.Length() != 4) || |
| (!request[1]->IsTypedData() && !request[1]->IsArray()) || |
| !request[2]->IsInt32OrInt64() || !request[3]->IsInt32OrInt64()) { |
| return CObject::IllegalArgumentError(); |
| } |
| if (file->IsClosed()) { |
| return CObject::FileClosedError(); |
| } |
| int64_t start = CObjectInt32OrInt64ToInt64(request[2]); |
| int64_t end = CObjectInt32OrInt64ToInt64(request[3]); |
| int64_t length = end - start; |
| const uint8_t* buffer_start; |
| if (request[1]->IsTypedData()) { |
| CObjectTypedData typed_data(request[1]); |
| start = start * SizeInBytes(typed_data.Type()); |
| length = length * SizeInBytes(typed_data.Type()); |
| buffer_start = typed_data.Buffer() + start; |
| } else { |
| CObjectArray array(request[1]); |
| uint8_t* allocated_buffer = Dart_ScopeAllocate(length); |
| buffer_start = allocated_buffer; |
| for (int i = 0; i < length; i++) { |
| if (array[i + start]->IsInt32OrInt64()) { |
| int64_t value = CObjectInt32OrInt64ToInt64(array[i + start]); |
| allocated_buffer[i] = static_cast<uint8_t>(value & 0xFF); |
| } else { |
| // Unsupported type. |
| return CObject::IllegalArgumentError(); |
| } |
| } |
| start = 0; |
| } |
| return file->WriteFully(reinterpret_cast<const void*>(buffer_start), length) |
| ? new CObjectInt64(CObject::NewInt64(length)) |
| : CObject::NewOSError(); |
| } |
| |
| CObject* File::CreateLinkRequest(const CObjectArray& request) { |
| if ((request.Length() != 3) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if (!request[1]->IsUint8Array() || !request[2]->IsString()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array link_name(request[1]); |
| CObjectString target_name(request[2]); |
| return File::CreateLink(namespc, |
| reinterpret_cast<const char*>(link_name.Buffer()), |
| target_name.CString()) |
| ? CObject::True() |
| : CObject::NewOSError(); |
| } |
| |
| CObject* File::DeleteLinkRequest(const CObjectArray& request) { |
| if ((request.Length() != 2) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if (!request[1]->IsUint8Array()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array link_path(request[1]); |
| return File::DeleteLink(namespc, |
| reinterpret_cast<const char*>(link_path.Buffer())) |
| ? CObject::True() |
| : CObject::NewOSError(); |
| } |
| |
| CObject* File::RenameLinkRequest(const CObjectArray& request) { |
| if ((request.Length() != 3) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if (!request[1]->IsUint8Array() || !request[2]->IsString()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array old_path(request[1]); |
| CObjectString new_path(request[2]); |
| return File::RenameLink(namespc, |
| reinterpret_cast<const char*>(old_path.Buffer()), |
| new_path.CString()) |
| ? CObject::True() |
| : CObject::NewOSError(); |
| } |
| |
| CObject* File::LinkTargetRequest(const CObjectArray& request) { |
| if ((request.Length() != 2) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if (!request[1]->IsUint8Array()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array link_path(request[1]); |
| const char* target = File::LinkTarget( |
| namespc, reinterpret_cast<const char*>(link_path.Buffer())); |
| if (target == nullptr) { |
| return CObject::NewOSError(); |
| } |
| return new CObjectString(CObject::NewString(target)); |
| } |
| |
| CObject* File::TypeRequest(const CObjectArray& request) { |
| if ((request.Length() != 3) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if (!request[1]->IsUint8Array() || !request[2]->IsBool()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectUint8Array path(request[1]); |
| CObjectBool follow_links(request[2]); |
| File::Type type = |
| File::GetType(namespc, reinterpret_cast<const char*>(path.Buffer()), |
| follow_links.Value()); |
| return new CObjectInt32(CObject::NewInt32(type)); |
| } |
| |
| CObject* File::IdenticalRequest(const CObjectArray& request) { |
| if ((request.Length() != 3) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if (!request[1]->IsString() || !request[2]->IsString()) { |
| return CObject::IllegalArgumentError(); |
| } |
| CObjectString path1(request[1]); |
| CObjectString path2(request[2]); |
| File::Identical result = |
| File::AreIdentical(namespc, path1.CString(), namespc, path2.CString()); |
| if (result == File::kError) { |
| return CObject::NewOSError(); |
| } |
| return (result == File::kIdentical) ? CObject::True() : CObject::False(); |
| } |
| |
| CObject* File::StatRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| Namespace* namespc = CObjectToNamespacePointer(request[0]); |
| RefCntReleaseScope<Namespace> rs(namespc); |
| if ((request.Length() != 2) || !request[1]->IsString()) { |
| return CObject::IllegalArgumentError(); |
| } |
| int64_t data[File::kStatSize]; |
| CObjectString path(request[1]); |
| File::Stat(namespc, path.CString(), data); |
| if (data[File::kType] == File::kDoesNotExist) { |
| return CObject::NewOSError(); |
| } |
| CObjectArray* result = new CObjectArray(CObject::NewArray(File::kStatSize)); |
| for (int i = 0; i < File::kStatSize; ++i) { |
| result->SetAt(i, new CObjectInt64(CObject::NewInt64(data[i]))); |
| } |
| CObjectArray* wrapper = new CObjectArray(CObject::NewArray(2)); |
| wrapper->SetAt(0, new CObjectInt32(CObject::NewInt32(CObject::kSuccess))); |
| wrapper->SetAt(1, result); |
| return wrapper; |
| } |
| |
| CObject* File::LockRequest(const CObjectArray& request) { |
| if ((request.Length() < 1) || !request[0]->IsIntptr()) { |
| return CObject::IllegalArgumentError(); |
| } |
| File* file = CObjectToFilePointer(request[0]); |
| RefCntReleaseScope<File> rs(file); |
| if ((request.Length() != 4) || !request[1]->IsInt32OrInt64() || |
| !request[2]->IsInt32OrInt64() || !request[3]->IsInt32OrInt64()) { |
| return CObject::IllegalArgumentError(); |
| } |
| if (file->IsClosed()) { |
| return CObject::FileClosedError(); |
| } |
| const int64_t lock = CObjectInt32OrInt64ToInt64(request[1]); |
| const int64_t start = CObjectInt32OrInt64ToInt64(request[2]); |
| const int64_t end = CObjectInt32OrInt64ToInt64(request[3]); |
| return file->Lock(static_cast<File::LockType>(lock), start, end) |
| ? CObject::True() |
| : CObject::NewOSError(); |
| } |
| |
| // Inspired by sdk/lib/core/uri.dart |
| UriDecoder::UriDecoder(const char* uri) : uri_(uri) { |
| const char* ch = uri; |
| while ((*ch != '\0') && (*ch != '%')) { |
| ch++; |
| } |
| if (*ch == 0) { |
| // if there are no '%', nothing to decode, refer to original as decoded. |
| decoded_ = const_cast<char*>(uri); |
| return; |
| } |
| const intptr_t len = strlen(uri); |
| // Decoded string should be shorter than original because of |
| // percent-encoding. |
| char* dest = reinterpret_cast<char*>(malloc(len + 1)); |
| int i = ch - uri; |
| // Copy all characters up to first '%' at index i. |
| strncpy(dest, uri, i); |
| decoded_ = dest; |
| dest += i; |
| while (*ch != '\0') { |
| if (*ch != '%') { |
| *(dest++) = *(ch++); |
| continue; |
| } |
| if ((i + 3 > len) || !HexCharPairToByte(ch + 1, dest)) { |
| free(decoded_); |
| decoded_ = nullptr; |
| return; |
| } |
| ++dest; |
| ch += 3; |
| } |
| *dest = 0; |
| } |
| |
| UriDecoder::~UriDecoder() { |
| if (uri_ != decoded_ && decoded_ != nullptr) { |
| free(decoded_); |
| } |
| } |
| |
| bool UriDecoder::HexCharPairToByte(const char* pch, char* const dest) { |
| int byte = 0; |
| for (int i = 0; i < 2; i++) { |
| char char_code = *(pch + i); |
| if (0x30 <= char_code && char_code <= 0x39) { |
| byte = byte * 16 + char_code - 0x30; |
| } else { |
| // Check ranges A-F (0x41-0x46) and a-f (0x61-0x66). |
| char_code |= 0x20; |
| if (0x61 <= char_code && char_code <= 0x66) { |
| byte = byte * 16 + char_code - 0x57; |
| } else { |
| return false; |
| } |
| } |
| } |
| *dest = byte; |
| return true; |
| } |
| |
| } // namespace bin |
| } // namespace dart |