mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 09:58:32 +00:00
fa7cadde80
I/O can take arbitrarily long, and open interior pointers prevent all other isolates in the group to reaching safepoints. TEST=ci Bug: https://github.com/flutter/flutter/issues/140763 Change-Id: I1fa732d0db31500946aee232dc31f5baf324a55e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/345746 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Brian Quinlan <bquinlan@google.com>
1486 lines
52 KiB
C++
1486 lines
52 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 "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);
|
|
if (is_byte_data) {
|
|
ThrowIfError(Dart_TypedDataReleaseData(buffer_obj));
|
|
}
|
|
if (bytes_read >= 0) {
|
|
if (!is_byte_data) {
|
|
ThrowIfError(Dart_ListSetAsBytes(buffer_obj, start, buffer, bytes_read));
|
|
}
|
|
Dart_SetIntegerReturnValue(args, bytes_read);
|
|
} else {
|
|
Dart_SetReturnValue(args, DartUtils::NewDartOSError());
|
|
}
|
|
}
|
|
|
|
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; // capture error if any
|
|
|
|
// Release the direct pointer acquired above.
|
|
ThrowIfError(Dart_TypedDataReleaseData(buffer_obj));
|
|
|
|
if (!success) {
|
|
Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
|
|
} else {
|
|
Dart_SetReturnValue(args, Dart_Null());
|
|
}
|
|
}
|
|
|
|
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::FreeIOBufferData(io_buffer);
|
|
return CObject::NewOSError();
|
|
}
|
|
|
|
// 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::FreeIOBufferData(io_buffer);
|
|
return CObject::NewOSError();
|
|
}
|
|
|
|
// 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
|