1// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "bin/file.h"
6
7#include <stdio.h>
8
9#include "bin/builtin.h"
10#include "bin/dartutils.h"
11#include "bin/io_buffer.h"
12#include "bin/namespace.h"
13#include "bin/typed_data_utils.h"
14#include "bin/utils.h"
15#include "include/bin/dart_io_api.h"
16#include "include/dart_api.h"
17#include "include/dart_tools_api.h"
18#include "platform/globals.h"
19
20namespace dart {
21namespace bin {
22
23static const int kFileNativeFieldIndex = 0;
24
25#if !defined(PRODUCT)
26static bool IsFile(Dart_Handle file_obj) {
27 Dart_Handle file_type = ThrowIfError(
28 DartUtils::GetDartType("dart:io", "_RandomAccessFileOpsImpl"));
29 bool isinstance = false;
30 ThrowIfError(Dart_ObjectIsType(file_obj, file_type, &isinstance));
31 return isinstance;
32}
33#endif
34
35// The file pointer has been passed into Dart as an intptr_t and it is safe
36// to pull it out of Dart as a 64-bit integer, cast it to an intptr_t and
37// from there to a File pointer.
38static File* GetFile(Dart_NativeArguments args) {
39 File* file;
40 Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
41 DEBUG_ASSERT(IsFile(dart_this));
42 Dart_Handle result = Dart_GetNativeInstanceField(
43 dart_this, kFileNativeFieldIndex, reinterpret_cast<intptr_t*>(&file));
44 ASSERT(!Dart_IsError(result));
45 if (file == NULL) {
46 Dart_PropagateError(Dart_NewUnhandledExceptionError(
47 DartUtils::NewInternalError("No native peer")));
48 }
49 return file;
50}
51
52static void SetFile(Dart_Handle dart_this, intptr_t file_pointer) {
53 DEBUG_ASSERT(IsFile(dart_this));
54 Dart_Handle result = Dart_SetNativeInstanceField(
55 dart_this, kFileNativeFieldIndex, file_pointer);
56 ThrowIfError(result);
57}
58
59void FUNCTION_NAME(File_GetPointer)(Dart_NativeArguments args) {
60 File* file = GetFile(args);
61 // If the file is already closed, GetFile() will return NULL.
62 if (file != NULL) {
63 // Increment file's reference count. File_GetPointer() should only be called
64 // when we are about to send the File* to the IO Service.
65 file->Retain();
66 }
67 intptr_t file_pointer = reinterpret_cast<intptr_t>(file);
68 Dart_SetIntegerReturnValue(args, file_pointer);
69}
70
71static void ReleaseFile(void* isolate_callback_data, void* peer) {
72 File* file = reinterpret_cast<File*>(peer);
73 file->Release();
74}
75
76void FUNCTION_NAME(File_SetPointer)(Dart_NativeArguments args) {
77 Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
78 intptr_t file_pointer = DartUtils::GetNativeIntptrArgument(args, 1);
79 File* file = reinterpret_cast<File*>(file_pointer);
80 Dart_FinalizableHandle handle = Dart_NewFinalizableHandle(
81 dart_this, reinterpret_cast<void*>(file), sizeof(*file), ReleaseFile);
82 file->SetFinalizableHandle(handle);
83 SetFile(dart_this, file_pointer);
84}
85
86void FUNCTION_NAME(File_Open)(Dart_NativeArguments args) {
87 Namespace* namespc = Namespace::GetNamespace(args, 0);
88 Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
89 File* file = NULL;
90 OSError os_error;
91 {
92 TypedDataScope data(path_handle);
93 ASSERT(data.type() == Dart_TypedData_kUint8);
94 const char* filename = data.GetCString();
95
96 int64_t mode = DartUtils::GetNativeIntegerArgument(args, 2);
97 File::DartFileOpenMode dart_file_mode =
98 static_cast<File::DartFileOpenMode>(mode);
99 File::FileOpenMode file_mode = File::DartModeToFileMode(dart_file_mode);
100 // Check that the file exists before opening it only for
101 // reading. This is to prevent the opening of directories as
102 // files. Directories can be opened for reading using the posix
103 // 'open' call.
104 file = File::Open(namespc, filename, file_mode);
105 if (file == NULL) {
106 // Errors must be caught before TypedDataScope data is destroyed.
107 os_error.Reload();
108 }
109 }
110 if (file != NULL) {
111 Dart_SetIntegerReturnValue(args, reinterpret_cast<intptr_t>(file));
112 } else {
113 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
114 }
115}
116
117void FUNCTION_NAME(File_Exists)(Dart_NativeArguments args) {
118 bool exists;
119 {
120 Namespace* namespc = Namespace::GetNamespace(args, 0);
121 Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
122 TypedDataScope data(path_handle);
123 ASSERT(data.type() == Dart_TypedData_kUint8);
124 const char* filename = data.GetCString();
125 exists = File::Exists(namespc, filename);
126 }
127 Dart_SetBooleanReturnValue(args, exists);
128}
129
130void FUNCTION_NAME(File_Close)(Dart_NativeArguments args) {
131 // TODO(zra): The bots are hitting a crash in this function, so we include
132 // some checks here that are normally only in a Debug build. When the crash
133 // is gone, this can go back to using GetFile and SetFile.
134 Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
135#if !defined(PRODUCT)
136 if (!IsFile(dart_this)) {
137 Dart_PropagateError(DartUtils::NewInternalError(
138 "File_Close expects the reciever to be a _RandomAccessFileOpsImpl."));
139 }
140#endif
141 File* file;
142 ThrowIfError(Dart_GetNativeInstanceField(dart_this, kFileNativeFieldIndex,
143 reinterpret_cast<intptr_t*>(&file)));
144 if (file == NULL) {
145 Dart_SetIntegerReturnValue(args, -1);
146 return;
147 }
148 file->Close();
149 file->DeleteFinalizableHandle(Dart_CurrentIsolate(), dart_this);
150 file->Release();
151
152 ThrowIfError(
153 Dart_SetNativeInstanceField(dart_this, kFileNativeFieldIndex, 0));
154 Dart_SetIntegerReturnValue(args, 0);
155}
156
157void FUNCTION_NAME(File_ReadByte)(Dart_NativeArguments args) {
158 File* file = GetFile(args);
159 ASSERT(file != NULL);
160 uint8_t buffer;
161 int64_t bytes_read = file->Read(reinterpret_cast<void*>(&buffer), 1);
162 if (bytes_read == 1) {
163 Dart_SetIntegerReturnValue(args, buffer);
164 } else if (bytes_read == 0) {
165 Dart_SetIntegerReturnValue(args, -1);
166 } else {
167 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
168 }
169}
170
171void FUNCTION_NAME(File_WriteByte)(Dart_NativeArguments args) {
172 File* file = GetFile(args);
173 ASSERT(file != NULL);
174 int64_t byte = 0;
175 if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &byte)) {
176 uint8_t buffer = static_cast<uint8_t>(byte & 0xff);
177 bool success = file->WriteFully(reinterpret_cast<void*>(&buffer), 1);
178 if (success) {
179 Dart_SetIntegerReturnValue(args, 1);
180 } else {
181 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
182 }
183 } else {
184 OSError os_error(-1, "Invalid argument", OSError::kUnknown);
185 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
186 }
187}
188
189void FUNCTION_NAME(File_Read)(Dart_NativeArguments args) {
190 File* file = GetFile(args);
191 ASSERT(file != NULL);
192 Dart_Handle length_object = Dart_GetNativeArgument(args, 1);
193 int64_t length = 0;
194 if (!DartUtils::GetInt64Value(length_object, &length) || (length < 0)) {
195 OSError os_error(-1, "Invalid argument", OSError::kUnknown);
196 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
197 return;
198 }
199 uint8_t* buffer = NULL;
200 Dart_Handle external_array = IOBuffer::Allocate(length, &buffer);
201 if (Dart_IsNull(external_array)) {
202 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
203 return;
204 }
205 int64_t bytes_read = file->Read(reinterpret_cast<void*>(buffer), length);
206 if (bytes_read < 0) {
207 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
208 return;
209 }
210 if (bytes_read < length) {
211 const int kNumArgs = 3;
212 Dart_Handle dart_args[kNumArgs];
213 dart_args[0] = external_array;
214 dart_args[1] = Dart_NewInteger(0);
215 dart_args[2] = Dart_NewInteger(bytes_read);
216 // TODO(sgjesse): Cache the _makeUint8ListView function somewhere.
217 Dart_Handle io_lib = Dart_LookupLibrary(DartUtils::NewString("dart:io"));
218 ThrowIfError(io_lib);
219 Dart_Handle array_view =
220 Dart_Invoke(io_lib, DartUtils::NewString("_makeUint8ListView"),
221 kNumArgs, dart_args);
222 Dart_SetReturnValue(args, array_view);
223 } else {
224 Dart_SetReturnValue(args, external_array);
225 }
226}
227
228void FUNCTION_NAME(File_ReadInto)(Dart_NativeArguments args) {
229 File* file = GetFile(args);
230 ASSERT(file != NULL);
231 Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1);
232 ASSERT(Dart_IsList(buffer_obj));
233 // start and end arguments are checked in Dart code to be
234 // integers and have the property that end <=
235 // list.length. Therefore, it is safe to extract their value as
236 // intptr_t.
237 intptr_t start = DartUtils::GetNativeIntptrArgument(args, 2);
238 intptr_t end = DartUtils::GetNativeIntptrArgument(args, 3);
239 intptr_t length = end - start;
240 intptr_t array_len = 0;
241 Dart_Handle result = Dart_ListLength(buffer_obj, &array_len);
242 ThrowIfError(result);
243 ASSERT(end <= array_len);
244 uint8_t* buffer = Dart_ScopeAllocate(length);
245 int64_t bytes_read = file->Read(reinterpret_cast<void*>(buffer), length);
246 if (bytes_read >= 0) {
247 result = Dart_ListSetAsBytes(buffer_obj, start, buffer, bytes_read);
248 if (Dart_IsError(result)) {
249 Dart_SetReturnValue(args, result);
250 } else {
251 Dart_SetIntegerReturnValue(args, bytes_read);
252 }
253 } else {
254 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
255 }
256}
257
258void FUNCTION_NAME(File_WriteFrom)(Dart_NativeArguments args) {
259 File* file = GetFile(args);
260 ASSERT(file != NULL);
261
262 Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1);
263
264 // Offset and length arguments are checked in Dart code to be
265 // integers and have the property that (offset + length) <=
266 // list.length. Therefore, it is safe to extract their value as
267 // intptr_t.
268 intptr_t start = DartUtils::GetNativeIntptrArgument(args, 2);
269 intptr_t end = DartUtils::GetNativeIntptrArgument(args, 3);
270
271 // The buffer object passed in has to be an Int8List or Uint8List object.
272 // Acquire a direct pointer to the data area of the buffer object.
273 Dart_TypedData_Type type;
274 intptr_t length = end - start;
275 intptr_t buffer_len = 0;
276 void* buffer = NULL;
277 Dart_Handle result =
278 Dart_TypedDataAcquireData(buffer_obj, &type, &buffer, &buffer_len);
279 ThrowIfError(result);
280
281 ASSERT(type == Dart_TypedData_kUint8 || type == Dart_TypedData_kInt8);
282 ASSERT(end <= buffer_len);
283 ASSERT(buffer != NULL);
284
285 // Write all the data out into the file.
286 char* byte_buffer = reinterpret_cast<char*>(buffer);
287 bool success = file->WriteFully(byte_buffer + start, length);
288
289 // Release the direct pointer acquired above.
290 ThrowIfError(Dart_TypedDataReleaseData(buffer_obj));
291 if (!success) {
292 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
293 } else {
294 Dart_SetReturnValue(args, Dart_Null());
295 }
296}
297
298void FUNCTION_NAME(File_Position)(Dart_NativeArguments args) {
299 File* file = GetFile(args);
300 ASSERT(file != NULL);
301 intptr_t return_value = file->Position();
302 if (return_value >= 0) {
303 Dart_SetIntegerReturnValue(args, return_value);
304 } else {
305 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
306 }
307}
308
309void FUNCTION_NAME(File_SetPosition)(Dart_NativeArguments args) {
310 File* file = GetFile(args);
311 ASSERT(file != NULL);
312 int64_t position = 0;
313 if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &position)) {
314 if (file->SetPosition(position)) {
315 Dart_SetBooleanReturnValue(args, true);
316 } else {
317 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
318 }
319 } else {
320 OSError os_error(-1, "Invalid argument", OSError::kUnknown);
321 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
322 }
323}
324
325void FUNCTION_NAME(File_Truncate)(Dart_NativeArguments args) {
326 File* file = GetFile(args);
327 ASSERT(file != NULL);
328 int64_t length = 0;
329 if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &length)) {
330 if (file->Truncate(length)) {
331 Dart_SetBooleanReturnValue(args, true);
332 } else {
333 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
334 }
335 } else {
336 OSError os_error(-1, "Invalid argument", OSError::kUnknown);
337 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
338 }
339}
340
341void FUNCTION_NAME(File_Length)(Dart_NativeArguments args) {
342 File* file = GetFile(args);
343 ASSERT(file != NULL);
344 int64_t return_value = file->Length();
345 if (return_value >= 0) {
346 Dart_SetIntegerReturnValue(args, return_value);
347 } else {
348 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
349 }
350}
351
352void FUNCTION_NAME(File_LengthFromPath)(Dart_NativeArguments args) {
353 Namespace* namespc = Namespace::GetNamespace(args, 0);
354 Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
355 int64_t return_value;
356 OSError os_error;
357 {
358 TypedDataScope data(path_handle);
359 ASSERT(data.type() == Dart_TypedData_kUint8);
360 const char* path = data.GetCString();
361 return_value = File::LengthFromPath(namespc, path);
362 if (return_value < 0) {
363 // Errors must be caught before TypedDataScope data is destroyed.
364 os_error.Reload();
365 }
366 }
367 if (return_value >= 0) {
368 Dart_SetIntegerReturnValue(args, return_value);
369 } else {
370 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
371 }
372}
373
374void FUNCTION_NAME(File_LastModified)(Dart_NativeArguments args) {
375 Namespace* namespc = Namespace::GetNamespace(args, 0);
376 Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
377 int64_t return_value;
378 OSError os_error;
379 {
380 TypedDataScope data(path_handle);
381 ASSERT(data.type() == Dart_TypedData_kUint8);
382 const char* raw_name = data.GetCString();
383 return_value = File::LastModified(namespc, raw_name);
384 if (return_value < 0) {
385 // Errors must be caught before TypedDataScope data is destroyed.
386 os_error.Reload();
387 }
388 }
389 if (return_value >= 0) {
390 Dart_SetIntegerReturnValue(args, return_value * kMillisecondsPerSecond);
391 } else {
392 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
393 }
394}
395
396void FUNCTION_NAME(File_SetLastModified)(Dart_NativeArguments args) {
397 Namespace* namespc = Namespace::GetNamespace(args, 0);
398 Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
399 int64_t millis;
400 if (!DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 2), &millis)) {
401 Dart_ThrowException(DartUtils::NewDartArgumentError(
402 "The second argument must be a 64-bit int."));
403 }
404 bool result;
405 OSError os_error;
406 {
407 TypedDataScope data(path_handle);
408 ASSERT(data.type() == Dart_TypedData_kUint8);
409 const char* name = data.GetCString();
410 result = File::SetLastModified(namespc, name, millis);
411 if (!result) {
412 // Errors must be caught before TypedDataScope data is destroyed.
413 os_error.Reload();
414 }
415 }
416 if (!result) {
417 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
418 }
419}
420
421void FUNCTION_NAME(File_LastAccessed)(Dart_NativeArguments args) {
422 Namespace* namespc = Namespace::GetNamespace(args, 0);
423 Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
424 int64_t return_value;
425 OSError os_error;
426 {
427 TypedDataScope data(path_handle);
428 ASSERT(data.type() == Dart_TypedData_kUint8);
429 const char* name = data.GetCString();
430 return_value = File::LastAccessed(namespc, name);
431 if (return_value < 0) {
432 // Errors must be caught before TypedDataScope data is destroyed.
433 os_error.Reload();
434 }
435 }
436 if (return_value >= 0) {
437 Dart_SetIntegerReturnValue(args, return_value * kMillisecondsPerSecond);
438 } else {
439 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
440 }
441}
442
443void FUNCTION_NAME(File_SetLastAccessed)(Dart_NativeArguments args) {
444 Namespace* namespc = Namespace::GetNamespace(args, 0);
445 Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
446 int64_t millis;
447 if (!DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 2), &millis)) {
448 Dart_ThrowException(DartUtils::NewDartArgumentError(
449 "The second argument must be a 64-bit int."));
450 }
451 bool result;
452 OSError os_error;
453 {
454 TypedDataScope data(path_handle);
455 ASSERT(data.type() == Dart_TypedData_kUint8);
456 const char* name = data.GetCString();
457 result = File::SetLastAccessed(namespc, name, millis);
458 if (!result) {
459 // Errors must be caught before TypedDataScope data is destroyed.
460 os_error.Reload();
461 }
462 }
463 if (!result) {
464 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
465 }
466}
467
468void FUNCTION_NAME(File_Flush)(Dart_NativeArguments args) {
469 File* file = GetFile(args);
470 ASSERT(file != NULL);
471 if (file->Flush()) {
472 Dart_SetBooleanReturnValue(args, true);
473 } else {
474 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
475 }
476}
477
478void FUNCTION_NAME(File_Lock)(Dart_NativeArguments args) {
479 File* file = GetFile(args);
480 ASSERT(file != NULL);
481 int64_t lock;
482 int64_t start;
483 int64_t end;
484 if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &lock) &&
485 DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 2), &start) &&
486 DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 3), &end)) {
487 if ((lock >= File::kLockMin) && (lock <= File::kLockMax) && (start >= 0) &&
488 (end == -1 || end > start)) {
489 if (file->Lock(static_cast<File::LockType>(lock), start, end)) {
490 Dart_SetBooleanReturnValue(args, true);
491 } else {
492 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
493 }
494 return;
495 }
496 }
497 OSError os_error(-1, "Invalid argument", OSError::kUnknown);
498 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
499}
500
501void FUNCTION_NAME(File_Create)(Dart_NativeArguments args) {
502 Namespace* namespc = Namespace::GetNamespace(args, 0);
503 Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
504 bool result;
505 OSError os_error;
506 {
507 TypedDataScope data(path_handle);
508 ASSERT(data.type() == Dart_TypedData_kUint8);
509 const char* path = data.GetCString();
510 result = File::Create(namespc, path);
511 if (!result) {
512 // Errors must be caught before TypedDataScope data is destroyed.
513 os_error.Reload();
514 }
515 }
516 if (result) {
517 Dart_SetBooleanReturnValue(args, result);
518 } else {
519 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
520 }
521}
522
523void FUNCTION_NAME(File_CreateLink)(Dart_NativeArguments args) {
524 Namespace* namespc = Namespace::GetNamespace(args, 0);
525 bool result;
526 Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
527 OSError os_error;
528 {
529 TypedDataScope data(path_handle);
530 ASSERT(data.type() == Dart_TypedData_kUint8);
531 const char* name = data.GetCString();
532 const char* target = DartUtils::GetNativeStringArgument(args, 2);
533 result = File::CreateLink(namespc, name, target);
534 if (!result) {
535 // Errors must be caught before TypedDataScope data is destroyed.
536 os_error.Reload();
537 }
538 }
539 if (!result) {
540 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
541 }
542}
543
544void FUNCTION_NAME(File_LinkTarget)(Dart_NativeArguments args) {
545 Namespace* namespc = Namespace::GetNamespace(args, 0);
546 Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
547 const char* target = NULL;
548 OSError os_error;
549 {
550 TypedDataScope data(path_handle);
551 ASSERT(data.type() == Dart_TypedData_kUint8);
552 const char* name = data.GetCString();
553 target = File::LinkTarget(namespc, name);
554 if (target == NULL) {
555 // Errors must be caught before TypedDataScope data is destroyed.
556 os_error.Reload();
557 }
558 }
559 if (target == NULL) {
560 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
561 } else {
562 Dart_Handle str = ThrowIfError(DartUtils::NewString(target));
563 Dart_SetReturnValue(args, str);
564 }
565}
566
567void FUNCTION_NAME(File_Delete)(Dart_NativeArguments args) {
568 Namespace* namespc = Namespace::GetNamespace(args, 0);
569 bool result;
570 Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
571 OSError os_error;
572 {
573 TypedDataScope data(path_handle);
574 ASSERT(data.type() == Dart_TypedData_kUint8);
575 const char* path = data.GetCString();
576 result = File::Delete(namespc, path);
577 if (!result) {
578 // Errors must be caught before TypedDataScope data is destroyed.
579 os_error.Reload();
580 }
581 }
582 if (result) {
583 Dart_SetBooleanReturnValue(args, result);
584 } else {
585 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
586 }
587}
588
589void FUNCTION_NAME(File_DeleteLink)(Dart_NativeArguments args) {
590 Namespace* namespc = Namespace::GetNamespace(args, 0);
591 Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
592 bool result;
593 OSError os_error;
594 {
595 TypedDataScope data(path_handle);
596 ASSERT(data.type() == Dart_TypedData_kUint8);
597 const char* path = data.GetCString();
598 result = File::DeleteLink(namespc, path);
599 if (!result) {
600 // Errors must be caught before TypedDataScope data is destroyed.
601 os_error.Reload();
602 }
603 }
604 if (result) {
605 Dart_SetBooleanReturnValue(args, result);
606 } else {
607 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
608 }
609}
610
611void FUNCTION_NAME(File_Rename)(Dart_NativeArguments args) {
612 Namespace* namespc = Namespace::GetNamespace(args, 0);
613 Dart_Handle old_path_handle = Dart_GetNativeArgument(args, 1);
614 bool result;
615 OSError os_error;
616 {
617 TypedDataScope old_path_data(old_path_handle);
618 ASSERT(old_path_data.type() == Dart_TypedData_kUint8);
619 const char* old_path = old_path_data.GetCString();
620 const char* new_path = DartUtils::GetNativeStringArgument(args, 2);
621 result = File::Rename(namespc, old_path, new_path);
622 if (!result) {
623 // Errors must be caught before TypedDataScope data is destroyed.
624 os_error.Reload();
625 }
626 }
627 if (result) {
628 Dart_SetBooleanReturnValue(args, result);
629 } else {
630 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
631 }
632}
633
634void FUNCTION_NAME(File_RenameLink)(Dart_NativeArguments args) {
635 Namespace* namespc = Namespace::GetNamespace(args, 0);
636 Dart_Handle old_path_handle = Dart_GetNativeArgument(args, 1);
637 bool result;
638 OSError os_error;
639 {
640 TypedDataScope old_path_data(old_path_handle);
641 ASSERT(old_path_data.type() == Dart_TypedData_kUint8);
642 const char* old_path = old_path_data.GetCString();
643 const char* new_path = DartUtils::GetNativeStringArgument(args, 2);
644 result = File::RenameLink(namespc, old_path, new_path);
645 if (!result) {
646 // Errors must be caught before TypedDataScope data is destroyed.
647 os_error.Reload();
648 }
649 }
650 if (result) {
651 Dart_SetBooleanReturnValue(args, result);
652 } else {
653 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
654 }
655}
656
657void FUNCTION_NAME(File_Copy)(Dart_NativeArguments args) {
658 Namespace* namespc = Namespace::GetNamespace(args, 0);
659 Dart_Handle old_path_handle = Dart_GetNativeArgument(args, 1);
660 bool result;
661 OSError os_error;
662 {
663 TypedDataScope old_path_data(old_path_handle);
664 ASSERT(old_path_data.type() == Dart_TypedData_kUint8);
665 const char* old_path = old_path_data.GetCString();
666 const char* new_path = DartUtils::GetNativeStringArgument(args, 2);
667 result = File::Copy(namespc, old_path, new_path);
668 if (!result) {
669 // Errors must be caught before TypedDataScope data is destroyed.
670 os_error.Reload();
671 }
672 }
673 if (result) {
674 Dart_SetBooleanReturnValue(args, result);
675 } else {
676 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
677 }
678}
679
680void FUNCTION_NAME(File_ResolveSymbolicLinks)(Dart_NativeArguments args) {
681 Namespace* namespc = Namespace::GetNamespace(args, 0);
682 Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
683 const char* path = NULL;
684 OSError os_error;
685 {
686 TypedDataScope data(path_handle);
687 ASSERT(data.type() == Dart_TypedData_kUint8);
688 const char* str = data.GetCString();
689 path = File::GetCanonicalPath(namespc, str);
690 if (path == NULL) {
691 // Errors must be caught before TypedDataScope data is destroyed.
692 os_error.Reload();
693 }
694 }
695 if (path != NULL) {
696 Dart_Handle str = ThrowIfError(DartUtils::NewString(path));
697 Dart_SetReturnValue(args, str);
698 } else {
699 Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
700 }
701}
702
703void FUNCTION_NAME(File_OpenStdio)(Dart_NativeArguments args) {
704 const int64_t fd = DartUtils::GetNativeIntegerArgument(args, 0);
705 File* file = File::OpenStdio(static_cast<int>(fd));
706 Dart_SetIntegerReturnValue(args, reinterpret_cast<intptr_t>(file));
707}
708
709void FUNCTION_NAME(File_GetStdioHandleType)(Dart_NativeArguments args) {
710 int64_t fd = DartUtils::GetNativeIntegerArgument(args, 0);
711 ASSERT((fd == STDIN_FILENO) || (fd == STDOUT_FILENO) ||
712 (fd == STDERR_FILENO));
713 File::StdioHandleType type = File::GetStdioHandleType(static_cast<int>(fd));
714 if (type == File::StdioHandleType::kTypeError) {
715 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
716 } else {
717 Dart_SetIntegerReturnValue(args, type);
718 }
719}
720
721void FUNCTION_NAME(File_GetType)(Dart_NativeArguments args) {
722 File::Type type;
723 {
724 Namespace* namespc = Namespace::GetNamespace(args, 0);
725 Dart_Handle path_handle = Dart_GetNativeArgument(args, 1);
726 TypedDataScope data(path_handle);
727 ASSERT(data.type() == Dart_TypedData_kUint8);
728 const char* path = data.GetCString();
729 bool follow_links = DartUtils::GetNativeBooleanArgument(args, 2);
730 type = File::GetType(namespc, path, follow_links);
731 }
732 Dart_SetIntegerReturnValue(args, static_cast<int>(type));
733}
734
735void FUNCTION_NAME(File_Stat)(Dart_NativeArguments args) {
736 Namespace* namespc = Namespace::GetNamespace(args, 0);
737 const char* path = DartUtils::GetNativeStringArgument(args, 1);
738
739 int64_t stat_data[File::kStatSize];
740 File::Stat(namespc, path, stat_data);
741 if (stat_data[File::kType] == File::kDoesNotExist) {
742 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
743 return;
744 }
745 Dart_Handle returned_data =
746 Dart_NewTypedData(Dart_TypedData_kInt64, File::kStatSize);
747 ThrowIfError(returned_data);
748 Dart_TypedData_Type data_type_unused;
749 void* data_location;
750 intptr_t data_length_unused;
751 Dart_Handle status = Dart_TypedDataAcquireData(
752 returned_data, &data_type_unused, &data_location, &data_length_unused);
753 ThrowIfError(status);
754 memmove(data_location, stat_data, File::kStatSize * sizeof(int64_t));
755 status = Dart_TypedDataReleaseData(returned_data);
756 ThrowIfError(status);
757 Dart_SetReturnValue(args, returned_data);
758}
759
760void FUNCTION_NAME(File_AreIdentical)(Dart_NativeArguments args) {
761 Namespace* namespc = Namespace::GetNamespace(args, 0);
762 const char* path_1 = DartUtils::GetNativeStringArgument(args, 1);
763 const char* path_2 = DartUtils::GetNativeStringArgument(args, 2);
764 File::Identical result = File::AreIdentical(namespc, path_1, namespc, path_2);
765 if (result == File::kError) {
766 Dart_SetReturnValue(args, DartUtils::NewDartOSError());
767 } else {
768 Dart_SetBooleanReturnValue(args, result == File::kIdentical);
769 }
770}
771
772#define IS_SEPARATOR(c) ((c) == '/' || (c) == 0)
773
774// Checks that if we increment this index forward, we'll still have enough space
775// for a null terminator within PATH_MAX bytes.
776#define CHECK_CAN_INCREMENT(i) \
777 if ((i) + 1 >= outlen) { \
778 return -1; \
779 }
780
781intptr_t File::CleanUnixPath(const char* in, char* out, intptr_t outlen) {
782 if (in[0] == 0) {
783 snprintf(out, outlen, ".");
784 return 1;
785 }
786
787 const bool rooted = (in[0] == '/');
788 intptr_t in_index = 0; // Index of the next byte to read.
789 intptr_t out_index = 0; // Index of the next byte to write.
790
791 if (rooted) {
792 out[out_index++] = '/';
793 in_index++;
794 }
795 // The output index at which '..' cannot be cleaned further.
796 intptr_t dotdot = out_index;
797
798 while (in[in_index] != 0) {
799 if (in[in_index] == '/') {
800 // 1. Reduce multiple slashes to a single slash.
801 CHECK_CAN_INCREMENT(in_index);
802 in_index++;
803 } else if ((in[in_index] == '.') && IS_SEPARATOR(in[in_index + 1])) {
804 // 2. Eliminate . path name elements (the current directory).
805 CHECK_CAN_INCREMENT(in_index);
806 in_index++;
807 } else if ((in[in_index] == '.') && (in[in_index + 1] == '.') &&
808 IS_SEPARATOR(in[in_index + 2])) {
809 CHECK_CAN_INCREMENT(in_index + 1);
810 in_index += 2;
811 if (out_index > dotdot) {
812 // 3. Eliminate .. path elements (the parent directory) and the element
813 // that precedes them.
814 out_index--;
815 while ((out_index > dotdot) && (out[out_index] != '/')) {
816 out_index--;
817 }
818 } else if (rooted) {
819 // 4. Eliminate .. elements that begin a rooted path, that is, replace
820 // /.. by / at the beginning of a path.
821 continue;
822 } else if (!rooted) {
823 if (out_index > 0) {
824 out[out_index++] = '/';
825 }
826 // 5. Leave intact .. elements that begin a non-rooted path.
827 out[out_index++] = '.';
828 out[out_index++] = '.';
829 dotdot = out_index;
830 }
831 } else {
832 if ((rooted && out_index != 1) || (!rooted && out_index != 0)) {
833 // Add '/' before normal path component, for non-root components.
834 out[out_index++] = '/';
835 }
836
837 while (!IS_SEPARATOR(in[in_index])) {
838 CHECK_CAN_INCREMENT(in_index);
839 out[out_index++] = in[in_index++];
840 }
841 }
842 }
843
844 if (out_index == 0) {
845 snprintf(out, outlen, ".");
846 return 1;
847 }
848
849 // Append null character.
850 out[out_index] = 0;
851 return out_index;
852}
853
854static int64_t CObjectInt32OrInt64ToInt64(CObject* cobject) {
855 ASSERT(cobject->IsInt32OrInt64());
856 int64_t result;
857 if (cobject->IsInt32()) {
858 CObjectInt32 value(cobject);
859 result = value.Value();
860 } else {
861 CObjectInt64 value(cobject);
862 result = value.Value();
863 }
864 return result;
865}
866
867static File* CObjectToFilePointer(CObject* cobject) {
868 CObjectIntptr value(cobject);
869 return reinterpret_cast<File*>(value.Value());
870}
871
872static Namespace* CObjectToNamespacePointer(CObject* cobject) {
873 CObjectIntptr value(cobject);
874 return reinterpret_cast<Namespace*>(value.Value());
875}
876
877CObject* File::ExistsRequest(const CObjectArray& request) {
878 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
879 return CObject::IllegalArgumentError();
880 }
881 Namespace* namespc = CObjectToNamespacePointer(request[0]);
882 RefCntReleaseScope<Namespace> rs(namespc);
883 if ((request.Length() != 2) || !request[1]->IsUint8Array()) {
884 return CObject::IllegalArgumentError();
885 }
886 CObjectUint8Array filename(request[1]);
887 return CObject::Bool(
888 File::Exists(namespc, reinterpret_cast<const char*>(filename.Buffer())));
889}
890
891CObject* File::CreateRequest(const CObjectArray& request) {
892 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
893 return CObject::IllegalArgumentError();
894 }
895 Namespace* namespc = CObjectToNamespacePointer(request[0]);
896 RefCntReleaseScope<Namespace> rs(namespc);
897 if ((request.Length() != 2) || !request[1]->IsUint8Array()) {
898 return CObject::IllegalArgumentError();
899 }
900 CObjectUint8Array filename(request[1]);
901 return File::Create(namespc, reinterpret_cast<const char*>(filename.Buffer()))
902 ? CObject::True()
903 : CObject::NewOSError();
904}
905
906CObject* File::OpenRequest(const CObjectArray& request) {
907 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
908 return CObject::IllegalArgumentError();
909 }
910 Namespace* namespc = CObjectToNamespacePointer(request[0]);
911 RefCntReleaseScope<Namespace> rs(namespc);
912 if ((request.Length() != 3) || !request[1]->IsUint8Array() ||
913 !request[2]->IsInt32()) {
914 return CObject::IllegalArgumentError();
915 }
916 CObjectUint8Array filename(request[1]);
917 CObjectInt32 mode(request[2]);
918 File::DartFileOpenMode dart_file_mode =
919 static_cast<File::DartFileOpenMode>(mode.Value());
920 File::FileOpenMode file_mode = File::DartModeToFileMode(dart_file_mode);
921 File* file = File::Open(
922 namespc, reinterpret_cast<const char*>(filename.Buffer()), file_mode);
923 if (file == NULL) {
924 return CObject::NewOSError();
925 }
926 return new CObjectIntptr(
927 CObject::NewIntptr(reinterpret_cast<intptr_t>(file)));
928}
929
930CObject* File::DeleteRequest(const CObjectArray& request) {
931 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
932 return CObject::False();
933 }
934 Namespace* namespc = CObjectToNamespacePointer(request[0]);
935 RefCntReleaseScope<Namespace> rs(namespc);
936 if ((request.Length() != 2) || !request[1]->IsUint8Array()) {
937 return CObject::False();
938 }
939 CObjectUint8Array filename(request[1]);
940 return File::Delete(namespc, reinterpret_cast<const char*>(filename.Buffer()))
941 ? CObject::True()
942 : CObject::NewOSError();
943}
944
945CObject* File::RenameRequest(const CObjectArray& request) {
946 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
947 return CObject::IllegalArgumentError();
948 }
949 Namespace* namespc = CObjectToNamespacePointer(request[0]);
950 RefCntReleaseScope<Namespace> rs(namespc);
951 if ((request.Length() != 3) || !request[1]->IsUint8Array() ||
952 !request[2]->IsString()) {
953 return CObject::IllegalArgumentError();
954 }
955 CObjectUint8Array old_path(request[1]);
956 CObjectString new_path(request[2]);
957 return File::Rename(namespc, reinterpret_cast<const char*>(old_path.Buffer()),
958 new_path.CString())
959 ? CObject::True()
960 : CObject::NewOSError();
961}
962
963CObject* File::CopyRequest(const CObjectArray& request) {
964 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
965 return CObject::IllegalArgumentError();
966 }
967 Namespace* namespc = CObjectToNamespacePointer(request[0]);
968 RefCntReleaseScope<Namespace> rs(namespc);
969 if ((request.Length() != 3) || !request[1]->IsUint8Array() ||
970 !request[2]->IsString()) {
971 return CObject::IllegalArgumentError();
972 }
973 CObjectUint8Array old_path(request[1]);
974 CObjectString new_path(request[2]);
975 return File::Copy(namespc, reinterpret_cast<const char*>(old_path.Buffer()),
976 new_path.CString())
977 ? CObject::True()
978 : CObject::NewOSError();
979}
980
981CObject* File::ResolveSymbolicLinksRequest(const CObjectArray& request) {
982 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
983 return CObject::IllegalArgumentError();
984 }
985 Namespace* namespc = CObjectToNamespacePointer(request[0]);
986 RefCntReleaseScope<Namespace> rs(namespc);
987 if ((request.Length() != 2) || !request[1]->IsUint8Array()) {
988 return CObject::IllegalArgumentError();
989 }
990 CObjectUint8Array filename(request[1]);
991 const char* result = File::GetCanonicalPath(
992 namespc, reinterpret_cast<const char*>(filename.Buffer()));
993 if (result == NULL) {
994 return CObject::NewOSError();
995 }
996 return new CObjectString(CObject::NewString(result));
997}
998
999CObject* File::CloseRequest(const CObjectArray& request) {
1000 if ((request.Length() != 1) || !request[0]->IsIntptr()) {
1001 return new CObjectIntptr(CObject::NewIntptr(-1));
1002 }
1003 File* file = CObjectToFilePointer(request[0]);
1004 RefCntReleaseScope<File> rs(file);
1005 // We have retained a reference to the file here. Therefore the file's
1006 // destructor can't be running. Since no further requests are dispatched by
1007 // the Dart code after an async close call, this Close() can't be racing
1008 // with any other call on the file. We don't do an extra Release(), and we
1009 // don't delete the weak persistent handle. The file is closed here, but the
1010 // memory will be cleaned up when the finalizer runs.
1011 ASSERT(!file->IsClosed());
1012 file->Close();
1013 return new CObjectIntptr(CObject::NewIntptr(0));
1014}
1015
1016CObject* File::PositionRequest(const CObjectArray& request) {
1017 if ((request.Length() != 1) || !request[0]->IsIntptr()) {
1018 return CObject::IllegalArgumentError();
1019 }
1020 File* file = CObjectToFilePointer(request[0]);
1021 RefCntReleaseScope<File> rs(file);
1022 if (file->IsClosed()) {
1023 return CObject::FileClosedError();
1024 }
1025 const intptr_t return_value = file->Position();
1026 if (return_value < 0) {
1027 return CObject::NewOSError();
1028 }
1029 return new CObjectIntptr(CObject::NewIntptr(return_value));
1030}
1031
1032CObject* File::SetPositionRequest(const CObjectArray& request) {
1033 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
1034 return CObject::IllegalArgumentError();
1035 }
1036 File* file = CObjectToFilePointer(request[0]);
1037 RefCntReleaseScope<File> rs(file);
1038 if ((request.Length() != 2) || !request[1]->IsInt32OrInt64()) {
1039 return CObject::IllegalArgumentError();
1040 }
1041 if (file->IsClosed()) {
1042 return CObject::FileClosedError();
1043 }
1044 const int64_t position = CObjectInt32OrInt64ToInt64(request[1]);
1045 return file->SetPosition(position) ? CObject::True() : CObject::NewOSError();
1046}
1047
1048CObject* File::TruncateRequest(const CObjectArray& request) {
1049 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
1050 return CObject::IllegalArgumentError();
1051 }
1052 File* file = CObjectToFilePointer(request[0]);
1053 RefCntReleaseScope<File> rs(file);
1054 if ((request.Length() != 2) || !request[1]->IsInt32OrInt64()) {
1055 return CObject::IllegalArgumentError();
1056 }
1057 if (file->IsClosed()) {
1058 return CObject::FileClosedError();
1059 }
1060 const int64_t length = CObjectInt32OrInt64ToInt64(request[1]);
1061 if (file->Truncate(length)) {
1062 return CObject::True();
1063 }
1064 return CObject::NewOSError();
1065}
1066
1067CObject* File::LengthRequest(const CObjectArray& request) {
1068 if ((request.Length() != 1) || !request[0]->IsIntptr()) {
1069 return CObject::IllegalArgumentError();
1070 }
1071 File* file = CObjectToFilePointer(request[0]);
1072 RefCntReleaseScope<File> rs(file);
1073 if (file->IsClosed()) {
1074 return CObject::FileClosedError();
1075 }
1076 const int64_t return_value = file->Length();
1077 if (return_value < 0) {
1078 return CObject::NewOSError();
1079 }
1080 return new CObjectInt64(CObject::NewInt64(return_value));
1081}
1082
1083CObject* File::LengthFromPathRequest(const CObjectArray& request) {
1084 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
1085 return CObject::IllegalArgumentError();
1086 }
1087 Namespace* namespc = CObjectToNamespacePointer(request[0]);
1088 RefCntReleaseScope<Namespace> rs(namespc);
1089 if ((request.Length() != 2) || !request[1]->IsUint8Array()) {
1090 return CObject::IllegalArgumentError();
1091 }
1092 CObjectUint8Array filepath(request[1]);
1093 const int64_t return_value = File::LengthFromPath(
1094 namespc, reinterpret_cast<const char*>(filepath.Buffer()));
1095 if (return_value < 0) {
1096 return CObject::NewOSError();
1097 }
1098 return new CObjectInt64(CObject::NewInt64(return_value));
1099}
1100
1101CObject* File::LastAccessedRequest(const CObjectArray& request) {
1102 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
1103 return CObject::IllegalArgumentError();
1104 }
1105 Namespace* namespc = CObjectToNamespacePointer(request[0]);
1106 RefCntReleaseScope<Namespace> rs(namespc);
1107 if ((request.Length() != 2) || !request[1]->IsUint8Array()) {
1108 return CObject::IllegalArgumentError();
1109 }
1110 CObjectUint8Array filepath(request[1]);
1111 const int64_t return_value = File::LastAccessed(
1112 namespc, reinterpret_cast<const char*>(filepath.Buffer()));
1113 if (return_value < 0) {
1114 return CObject::NewOSError();
1115 }
1116 return new CObjectIntptr(
1117 CObject::NewInt64(return_value * kMillisecondsPerSecond));
1118}
1119
1120CObject* File::SetLastAccessedRequest(const CObjectArray& request) {
1121 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
1122 return CObject::IllegalArgumentError();
1123 }
1124 Namespace* namespc = CObjectToNamespacePointer(request[0]);
1125 RefCntReleaseScope<Namespace> rs(namespc);
1126 if ((request.Length() != 3) || !request[1]->IsUint8Array() ||
1127 !request[2]->IsInt32OrInt64()) {
1128 return CObject::IllegalArgumentError();
1129 }
1130 CObjectUint8Array filepath(request[1]);
1131 const int64_t millis = CObjectInt32OrInt64ToInt64(request[2]);
1132 return File::SetLastAccessed(
1133 namespc, reinterpret_cast<const char*>(filepath.Buffer()), millis)
1134 ? CObject::Null()
1135 : CObject::NewOSError();
1136}
1137
1138CObject* File::LastModifiedRequest(const CObjectArray& request) {
1139 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
1140 return CObject::IllegalArgumentError();
1141 }
1142 Namespace* namespc = CObjectToNamespacePointer(request[0]);
1143 RefCntReleaseScope<Namespace> rs(namespc);
1144 if ((request.Length() != 2) || !request[1]->IsUint8Array()) {
1145 return CObject::IllegalArgumentError();
1146 }
1147 CObjectUint8Array filepath(request[1]);
1148 const int64_t return_value = File::LastModified(
1149 namespc, reinterpret_cast<const char*>(filepath.Buffer()));
1150 if (return_value < 0) {
1151 return CObject::NewOSError();
1152 }
1153 return new CObjectIntptr(
1154 CObject::NewInt64(return_value * kMillisecondsPerSecond));
1155}
1156
1157CObject* File::SetLastModifiedRequest(const CObjectArray& request) {
1158 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
1159 return CObject::IllegalArgumentError();
1160 }
1161 Namespace* namespc = CObjectToNamespacePointer(request[0]);
1162 RefCntReleaseScope<Namespace> rs(namespc);
1163 if ((request.Length() != 3) || !request[1]->IsUint8Array() ||
1164 !request[2]->IsInt32OrInt64()) {
1165 return CObject::IllegalArgumentError();
1166 }
1167 CObjectUint8Array filepath(request[1]);
1168 const int64_t millis = CObjectInt32OrInt64ToInt64(request[2]);
1169 return File::SetLastModified(
1170 namespc, reinterpret_cast<const char*>(filepath.Buffer()), millis)
1171 ? CObject::Null()
1172 : CObject::NewOSError();
1173}
1174
1175CObject* File::FlushRequest(const CObjectArray& request) {
1176 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
1177 return CObject::IllegalArgumentError();
1178 }
1179 File* file = CObjectToFilePointer(request[0]);
1180 RefCntReleaseScope<File> rs(file);
1181 if (file->IsClosed()) {
1182 return CObject::FileClosedError();
1183 }
1184 return file->Flush() ? CObject::True() : CObject::NewOSError();
1185}
1186
1187CObject* File::ReadByteRequest(const CObjectArray& request) {
1188 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
1189 return CObject::IllegalArgumentError();
1190 }
1191 File* file = CObjectToFilePointer(request[0]);
1192 RefCntReleaseScope<File> rs(file);
1193 if (file->IsClosed()) {
1194 return CObject::FileClosedError();
1195 }
1196 uint8_t buffer;
1197 const int64_t bytes_read = file->Read(reinterpret_cast<void*>(&buffer), 1);
1198 if (bytes_read < 0) {
1199 return CObject::NewOSError();
1200 }
1201 if (bytes_read == 0) {
1202 return new CObjectIntptr(CObject::NewIntptr(-1));
1203 }
1204 return new CObjectIntptr(CObject::NewIntptr(buffer));
1205}
1206
1207CObject* File::WriteByteRequest(const CObjectArray& request) {
1208 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
1209 return CObject::IllegalArgumentError();
1210 }
1211 File* file = CObjectToFilePointer(request[0]);
1212 RefCntReleaseScope<File> rs(file);
1213 if ((request.Length() != 2) || !request[1]->IsInt32OrInt64()) {
1214 return CObject::IllegalArgumentError();
1215 }
1216 if (file->IsClosed()) {
1217 return CObject::FileClosedError();
1218 }
1219 const int64_t byte = CObjectInt32OrInt64ToInt64(request[1]);
1220 uint8_t buffer = static_cast<uint8_t>(byte & 0xff);
1221 return file->WriteFully(reinterpret_cast<void*>(&buffer), 1)
1222 ? new CObjectInt64(CObject::NewInt64(1))
1223 : CObject::NewOSError();
1224}
1225
1226CObject* File::ReadRequest(const CObjectArray& request) {
1227 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
1228 return CObject::IllegalArgumentError();
1229 }
1230 File* file = CObjectToFilePointer(request[0]);
1231 RefCntReleaseScope<File> rs(file);
1232 if ((request.Length() != 2) || !request[1]->IsInt32OrInt64()) {
1233 return CObject::IllegalArgumentError();
1234 }
1235 if (file->IsClosed()) {
1236 return CObject::FileClosedError();
1237 }
1238 const int64_t length = CObjectInt32OrInt64ToInt64(request[1]);
1239 Dart_CObject* io_buffer = CObject::NewIOBuffer(length);
1240 if (io_buffer == NULL) {
1241 return CObject::NewOSError();
1242 }
1243 uint8_t* data = io_buffer->value.as_external_typed_data.data;
1244 const int64_t bytes_read = file->Read(data, length);
1245 if (bytes_read < 0) {
1246 CObject::FreeIOBufferData(io_buffer);
1247 return CObject::NewOSError();
1248 }
1249 CObjectExternalUint8Array* external_array =
1250 new CObjectExternalUint8Array(io_buffer);
1251 external_array->SetLength(bytes_read);
1252 CObjectArray* result = new CObjectArray(CObject::NewArray(2));
1253 result->SetAt(0, new CObjectIntptr(CObject::NewInt32(0)));
1254 result->SetAt(1, external_array);
1255 return result;
1256}
1257
1258CObject* File::ReadIntoRequest(const CObjectArray& request) {
1259 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
1260 return CObject::IllegalArgumentError();
1261 }
1262 File* file = CObjectToFilePointer(request[0]);
1263 RefCntReleaseScope<File> rs(file);
1264 if ((request.Length() != 2) || !request[1]->IsInt32OrInt64()) {
1265 return CObject::IllegalArgumentError();
1266 }
1267 if (file->IsClosed()) {
1268 return CObject::FileClosedError();
1269 }
1270 const int64_t length = CObjectInt32OrInt64ToInt64(request[1]);
1271 Dart_CObject* io_buffer = CObject::NewIOBuffer(length);
1272 if (io_buffer == NULL) {
1273 return CObject::NewOSError();
1274 }
1275 uint8_t* data = io_buffer->value.as_external_typed_data.data;
1276 const int64_t bytes_read = file->Read(data, length);
1277 if (bytes_read < 0) {
1278 CObject::FreeIOBufferData(io_buffer);
1279 return CObject::NewOSError();
1280 }
1281 CObjectExternalUint8Array* external_array =
1282 new CObjectExternalUint8Array(io_buffer);
1283 external_array->SetLength(bytes_read);
1284 CObjectArray* result = new CObjectArray(CObject::NewArray(3));
1285 result->SetAt(0, new CObjectIntptr(CObject::NewInt32(0)));
1286 result->SetAt(1, new CObjectInt64(CObject::NewInt64(bytes_read)));
1287 result->SetAt(2, external_array);
1288 return result;
1289}
1290
1291static int SizeInBytes(Dart_TypedData_Type type) {
1292 switch (type) {
1293 case Dart_TypedData_kInt8:
1294 case Dart_TypedData_kUint8:
1295 case Dart_TypedData_kUint8Clamped:
1296 return 1;
1297 case Dart_TypedData_kInt16:
1298 case Dart_TypedData_kUint16:
1299 return 2;
1300 case Dart_TypedData_kInt32:
1301 case Dart_TypedData_kUint32:
1302 case Dart_TypedData_kFloat32:
1303 return 4;
1304 case Dart_TypedData_kInt64:
1305 case Dart_TypedData_kUint64:
1306 case Dart_TypedData_kFloat64:
1307 return 8;
1308 case Dart_TypedData_kInt32x4:
1309 case Dart_TypedData_kFloat32x4:
1310 case Dart_TypedData_kFloat64x2:
1311 return 16;
1312 default:
1313 break;
1314 }
1315 UNREACHABLE();
1316 return -1;
1317}
1318
1319CObject* File::WriteFromRequest(const CObjectArray& request) {
1320 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
1321 return CObject::IllegalArgumentError();
1322 }
1323 File* file = CObjectToFilePointer(request[0]);
1324 RefCntReleaseScope<File> rs(file);
1325 if ((request.Length() != 4) ||
1326 (!request[1]->IsTypedData() && !request[1]->IsArray()) ||
1327 !request[2]->IsInt32OrInt64() || !request[3]->IsInt32OrInt64()) {
1328 return CObject::IllegalArgumentError();
1329 }
1330 if (file->IsClosed()) {
1331 return CObject::FileClosedError();
1332 }
1333 int64_t start = CObjectInt32OrInt64ToInt64(request[2]);
1334 int64_t end = CObjectInt32OrInt64ToInt64(request[3]);
1335 int64_t length = end - start;
1336 uint8_t* buffer_start;
1337 if (request[1]->IsTypedData()) {
1338 CObjectTypedData typed_data(request[1]);
1339 start = start * SizeInBytes(typed_data.Type());
1340 length = length * SizeInBytes(typed_data.Type());
1341 buffer_start = typed_data.Buffer() + start;
1342 } else {
1343 CObjectArray array(request[1]);
1344 buffer_start = Dart_ScopeAllocate(length);
1345 for (int i = 0; i < length; i++) {
1346 if (array[i + start]->IsInt32OrInt64()) {
1347 int64_t value = CObjectInt32OrInt64ToInt64(array[i + start]);
1348 buffer_start[i] = static_cast<uint8_t>(value & 0xFF);
1349 } else {
1350 // Unsupported type.
1351 return CObject::IllegalArgumentError();
1352 }
1353 }
1354 start = 0;
1355 }
1356 return file->WriteFully(reinterpret_cast<void*>(buffer_start), length)
1357 ? new CObjectInt64(CObject::NewInt64(length))
1358 : CObject::NewOSError();
1359}
1360
1361CObject* File::CreateLinkRequest(const CObjectArray& request) {
1362 if ((request.Length() != 3) || !request[0]->IsIntptr()) {
1363 return CObject::IllegalArgumentError();
1364 }
1365 Namespace* namespc = CObjectToNamespacePointer(request[0]);
1366 RefCntReleaseScope<Namespace> rs(namespc);
1367 if (!request[1]->IsUint8Array() || !request[2]->IsString()) {
1368 return CObject::IllegalArgumentError();
1369 }
1370 CObjectUint8Array link_name(request[1]);
1371 CObjectString target_name(request[2]);
1372 return File::CreateLink(namespc,
1373 reinterpret_cast<const char*>(link_name.Buffer()),
1374 target_name.CString())
1375 ? CObject::True()
1376 : CObject::NewOSError();
1377}
1378
1379CObject* File::DeleteLinkRequest(const CObjectArray& request) {
1380 if ((request.Length() != 2) || !request[0]->IsIntptr()) {
1381 return CObject::IllegalArgumentError();
1382 }
1383 Namespace* namespc = CObjectToNamespacePointer(request[0]);
1384 RefCntReleaseScope<Namespace> rs(namespc);
1385 if (!request[1]->IsUint8Array()) {
1386 return CObject::IllegalArgumentError();
1387 }
1388 CObjectUint8Array link_path(request[1]);
1389 return File::DeleteLink(namespc,
1390 reinterpret_cast<const char*>(link_path.Buffer()))
1391 ? CObject::True()
1392 : CObject::NewOSError();
1393}
1394
1395CObject* File::RenameLinkRequest(const CObjectArray& request) {
1396 if ((request.Length() != 3) || !request[0]->IsIntptr()) {
1397 return CObject::IllegalArgumentError();
1398 }
1399 Namespace* namespc = CObjectToNamespacePointer(request[0]);
1400 RefCntReleaseScope<Namespace> rs(namespc);
1401 if (!request[1]->IsUint8Array() || !request[2]->IsString()) {
1402 return CObject::IllegalArgumentError();
1403 }
1404 CObjectUint8Array old_path(request[1]);
1405 CObjectString new_path(request[2]);
1406 return File::RenameLink(namespc,
1407 reinterpret_cast<const char*>(old_path.Buffer()),
1408 new_path.CString())
1409 ? CObject::True()
1410 : CObject::NewOSError();
1411}
1412
1413CObject* File::LinkTargetRequest(const CObjectArray& request) {
1414 if ((request.Length() != 2) || !request[0]->IsIntptr()) {
1415 return CObject::IllegalArgumentError();
1416 }
1417 Namespace* namespc = CObjectToNamespacePointer(request[0]);
1418 RefCntReleaseScope<Namespace> rs(namespc);
1419 if (!request[1]->IsUint8Array()) {
1420 return CObject::IllegalArgumentError();
1421 }
1422 CObjectUint8Array link_path(request[1]);
1423 const char* target = File::LinkTarget(
1424 namespc, reinterpret_cast<const char*>(link_path.Buffer()));
1425 if (target == NULL) {
1426 return CObject::NewOSError();
1427 }
1428 return new CObjectString(CObject::NewString(target));
1429}
1430
1431CObject* File::TypeRequest(const CObjectArray& request) {
1432 if ((request.Length() != 3) || !request[0]->IsIntptr()) {
1433 return CObject::IllegalArgumentError();
1434 }
1435 Namespace* namespc = CObjectToNamespacePointer(request[0]);
1436 RefCntReleaseScope<Namespace> rs(namespc);
1437 if (!request[1]->IsUint8Array() || !request[2]->IsBool()) {
1438 return CObject::IllegalArgumentError();
1439 }
1440 CObjectUint8Array path(request[1]);
1441 CObjectBool follow_links(request[2]);
1442 File::Type type =
1443 File::GetType(namespc, reinterpret_cast<const char*>(path.Buffer()),
1444 follow_links.Value());
1445 return new CObjectInt32(CObject::NewInt32(type));
1446}
1447
1448CObject* File::IdenticalRequest(const CObjectArray& request) {
1449 if ((request.Length() != 3) || !request[0]->IsIntptr()) {
1450 return CObject::IllegalArgumentError();
1451 }
1452 Namespace* namespc = CObjectToNamespacePointer(request[0]);
1453 RefCntReleaseScope<Namespace> rs(namespc);
1454 if (!request[1]->IsString() || !request[2]->IsString()) {
1455 return CObject::IllegalArgumentError();
1456 }
1457 CObjectString path1(request[1]);
1458 CObjectString path2(request[2]);
1459 File::Identical result =
1460 File::AreIdentical(namespc, path1.CString(), namespc, path2.CString());
1461 if (result == File::kError) {
1462 return CObject::NewOSError();
1463 }
1464 return (result == File::kIdentical) ? CObject::True() : CObject::False();
1465}
1466
1467CObject* File::StatRequest(const CObjectArray& request) {
1468 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
1469 return CObject::IllegalArgumentError();
1470 }
1471 Namespace* namespc = CObjectToNamespacePointer(request[0]);
1472 RefCntReleaseScope<Namespace> rs(namespc);
1473 if ((request.Length() != 2) || !request[1]->IsString()) {
1474 return CObject::IllegalArgumentError();
1475 }
1476 int64_t data[File::kStatSize];
1477 CObjectString path(request[1]);
1478 File::Stat(namespc, path.CString(), data);
1479 if (data[File::kType] == File::kDoesNotExist) {
1480 return CObject::NewOSError();
1481 }
1482 CObjectArray* result = new CObjectArray(CObject::NewArray(File::kStatSize));
1483 for (int i = 0; i < File::kStatSize; ++i) {
1484 result->SetAt(i, new CObjectInt64(CObject::NewInt64(data[i])));
1485 }
1486 CObjectArray* wrapper = new CObjectArray(CObject::NewArray(2));
1487 wrapper->SetAt(0, new CObjectInt32(CObject::NewInt32(CObject::kSuccess)));
1488 wrapper->SetAt(1, result);
1489 return wrapper;
1490}
1491
1492CObject* File::LockRequest(const CObjectArray& request) {
1493 if ((request.Length() < 1) || !request[0]->IsIntptr()) {
1494 return CObject::IllegalArgumentError();
1495 }
1496 File* file = CObjectToFilePointer(request[0]);
1497 RefCntReleaseScope<File> rs(file);
1498 if ((request.Length() != 4) || !request[1]->IsInt32OrInt64() ||
1499 !request[2]->IsInt32OrInt64() || !request[3]->IsInt32OrInt64()) {
1500 return CObject::IllegalArgumentError();
1501 }
1502 if (file->IsClosed()) {
1503 return CObject::FileClosedError();
1504 }
1505 const int64_t lock = CObjectInt32OrInt64ToInt64(request[1]);
1506 const int64_t start = CObjectInt32OrInt64ToInt64(request[2]);
1507 const int64_t end = CObjectInt32OrInt64ToInt64(request[3]);
1508 return file->Lock(static_cast<File::LockType>(lock), start, end)
1509 ? CObject::True()
1510 : CObject::NewOSError();
1511}
1512
1513// Inspired by sdk/lib/core/uri.dart
1514UriDecoder::UriDecoder(const char* uri) : uri_(uri) {
1515 const char* ch = uri;
1516 while ((*ch != '\0') && (*ch != '%')) {
1517 ch++;
1518 }
1519 if (*ch == 0) {
1520 // if there are no '%', nothing to decode, refer to original as decoded.
1521 decoded_ = const_cast<char*>(uri);
1522 return;
1523 }
1524 const intptr_t len = strlen(uri);
1525 // Decoded string should be shorter than original because of
1526 // percent-encoding.
1527 char* dest = reinterpret_cast<char*>(malloc(len + 1));
1528 int i = ch - uri;
1529 // Copy all characters up to first '%' at index i.
1530 strncpy(dest, uri, i);
1531 decoded_ = dest;
1532 dest += i;
1533 while (*ch != '\0') {
1534 if (*ch != '%') {
1535 *(dest++) = *(ch++);
1536 continue;
1537 }
1538 if ((i + 3 > len) || !HexCharPairToByte(ch + 1, dest)) {
1539 free(decoded_);
1540 decoded_ = NULL;
1541 return;
1542 }
1543 ++dest;
1544 ch += 3;
1545 }
1546 *dest = 0;
1547}
1548
1549UriDecoder::~UriDecoder() {
1550 if (uri_ != decoded_ && decoded_ != NULL) {
1551 free(decoded_);
1552 }
1553}
1554
1555bool UriDecoder::HexCharPairToByte(const char* pch, char* const dest) {
1556 int byte = 0;
1557 for (int i = 0; i < 2; i++) {
1558 char char_code = *(pch + i);
1559 if (0x30 <= char_code && char_code <= 0x39) {
1560 byte = byte * 16 + char_code - 0x30;
1561 } else {
1562 // Check ranges A-F (0x41-0x46) and a-f (0x61-0x66).
1563 char_code |= 0x20;
1564 if (0x61 <= char_code && char_code <= 0x66) {
1565 byte = byte * 16 + char_code - 0x57;
1566 } else {
1567 return false;
1568 }
1569 }
1570 }
1571 *dest = byte;
1572 return true;
1573}
1574
1575} // namespace bin
1576} // namespace dart
1577