OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2016, 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 <map> | |
6 #include <string> | |
7 #include <vector> | |
8 | |
9 #include "vm/dev_fs.h" | |
10 | |
11 #include "vm/hash_table.h" | |
12 #include "vm/json_stream.h" | |
13 #include "vm/lockers.h" | |
14 #include "vm/object.h" | |
15 #include "vm/unicode.h" | |
16 | |
17 namespace dart { | |
18 | |
19 static const uint8_t decode_table[256] = { | |
20 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
21 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
22 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, | |
23 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, | |
24 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | |
25 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, | |
26 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, | |
27 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, | |
28 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
29 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
30 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
31 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
32 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
33 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
34 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, | |
35 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 | |
36 }; | |
37 | |
38 class Base64 { | |
39 public: | |
40 static void decode(const char* base64, | |
41 std::vector<uint8_t>* output) { | |
42 ASSERT(output != NULL); | |
43 ASSERT(base64 != NULL); | |
44 const intptr_t base64_len = strlen(base64); | |
45 int b[4]; | |
46 for (intptr_t i = 0; i < base64_len; i += 4) { | |
47 b[0] = decode_table[static_cast<uint8_t>(base64[i])]; | |
48 b[1] = decode_table[static_cast<uint8_t>(base64[i + 1])]; | |
49 b[2] = decode_table[static_cast<uint8_t>(base64[i + 2])]; | |
50 b[3] = decode_table[static_cast<uint8_t>(base64[i + 3])]; | |
51 output->push_back((b[0] << 2) | (b[1] >> 4)); | |
52 if (b[2] < 64) { | |
53 output->push_back((b[1] << 4) | (b[2] >> 2)); | |
54 if (b[3] < 64) { | |
55 output->push_back((b[2] << 6) | b[3]); | |
56 } | |
57 } | |
58 } | |
59 } | |
60 }; | |
61 | |
62 | |
63 class FileSystem { | |
64 public: | |
65 explicit FileSystem(const std::string& name) | |
66 : name_(name) { | |
67 } | |
68 | |
69 ~FileSystem() { | |
70 } | |
71 | |
72 bool ReadFile(const std::string& path, | |
73 std::vector<uint8_t>** file_contents) { | |
74 *file_contents = NULL; | |
75 std::map<std::string, std::vector<uint8_t>*>::iterator iter; | |
76 iter = files_.find(path); | |
77 if (iter == files_.end()) { | |
78 return false; | |
79 } | |
80 *file_contents = iter->second; | |
81 return true; | |
82 } | |
83 | |
84 void DeleteFile(const std::string& path) { | |
85 std::map<std::string, std::vector<uint8_t>*>::iterator iter; | |
86 iter = files_.find(path); | |
87 if (iter == files_.end()) { | |
88 return; | |
89 } | |
90 std::vector<uint8_t>* contents = iter->second; | |
91 files_.erase(iter); | |
92 delete contents; | |
93 } | |
94 | |
95 void WriteFile(const std::string& path, | |
96 const char* file_contents) { | |
97 DeleteFile(path); | |
98 std::vector<uint8_t>* data = new std::vector<uint8_t>(); | |
99 Base64::decode(file_contents, data); | |
100 files_[path] = data; | |
101 } | |
102 | |
103 void WriteFileRaw(const std::string& path, | |
104 const char* file_contents) { | |
105 DeleteFile(path); | |
106 std::vector<uint8_t>* data = new std::vector<uint8_t>(); | |
107 Base64::decode(file_contents, data); | |
108 files_[path] = data; | |
109 } | |
110 | |
111 void ListFiles(JSONStream* js) { | |
112 JSONObject jsobj(js); | |
113 jsobj.AddProperty("type", "FSFilesList"); | |
114 JSONArray jsarr(&jsobj, "files"); | |
115 std::map<std::string, std::vector<uint8_t>*>::iterator iter; | |
116 for (iter = files_.begin(); iter != files_.end(); iter++) { | |
117 JSONObject file_info(&jsarr); | |
118 file_info.AddProperty("name", iter->first.c_str()); | |
119 file_info.AddProperty("size", static_cast<int64_t>(iter->second->size())); | |
120 } | |
121 } | |
122 | |
123 private: | |
124 std::string name_; | |
125 | |
126 std::map<std::string, std::vector<uint8_t>*> files_; | |
127 }; | |
128 | |
129 // Some static state is held outside of the DevFS class so that we don't | |
130 // have to include stl headers in our vm/ headers. | |
131 static std::map<std::string, FileSystem*>* file_systems_; | |
132 | |
133 Mutex* DevFS::mutex_ = NULL; | |
134 | |
135 | |
136 void DevFS::Init() { | |
137 if (mutex_ != NULL) { | |
138 // Already initialized. | |
139 ASSERT(file_systems_ != NULL); | |
140 return; | |
141 } | |
142 mutex_ = new Mutex(); | |
143 file_systems_ = new std::map<std::string, FileSystem*>(); | |
144 ASSERT(mutex_ != NULL); | |
145 ASSERT(file_systems_ != NULL); | |
146 } | |
147 | |
148 | |
149 void DevFS::Cleanup() { | |
150 delete mutex_; | |
151 mutex_ = NULL; | |
152 std::map<std::string, FileSystem*>::iterator iter; | |
153 for (iter = file_systems_->begin(); iter != file_systems_->end(); iter++) { | |
154 FileSystem* fs = iter->second; | |
155 delete fs; | |
156 } | |
157 delete file_systems_; | |
158 file_systems_ = NULL; | |
159 } | |
160 | |
161 | |
162 void DevFS::ListFileSystems(JSONStream* js) { | |
163 SafepointMutexLocker ml(mutex_); | |
164 JSONObject jsobj(js); | |
165 jsobj.AddProperty("type", "FSList"); | |
166 JSONArray jsarr(&jsobj, "ids"); | |
turnidge
2016/06/10 18:45:34
Do we want to call this ids still? Maybe fsNames?
| |
167 | |
168 std::map<std::string, FileSystem*>::iterator iter; | |
169 for (iter = file_systems_->begin(); iter != file_systems_->end(); iter++) { | |
170 const std::string& key = iter->first; | |
171 jsarr.AddValue(key.c_str()); | |
172 } | |
173 } | |
174 | |
175 | |
176 FileSystem* DevFS::LookupFileSystem(const char* fs_name) { | |
177 std::string key = std::string(fs_name); | |
178 std::map<std::string, FileSystem*>::iterator iter; | |
179 iter = file_systems_->find(key); | |
180 if (iter != file_systems_->end()) { | |
181 return iter->second; | |
182 } | |
183 return NULL; | |
184 } | |
185 | |
186 | |
187 FileSystem* DevFS::LookupFileSystem(const String& fs_name) { | |
188 return LookupFileSystem(fs_name.ToCString()); | |
189 } | |
190 | |
191 | |
192 void DevFS::CreateFileSystem(JSONStream* js, const String& fs_name) { | |
193 SafepointMutexLocker ml(mutex_); | |
194 // TODO(turnidge): Ensure that fs_name is a legal URI host value, i.e. ascii. | |
195 if (LookupFileSystem(fs_name) != NULL) { | |
196 js->PrintError(kFileSystemAlreadyExists, | |
197 "%s: file system '%s' already exists", | |
198 js->method(), fs_name.ToCString()); | |
199 return; | |
200 } | |
201 | |
202 std::string key = std::string(fs_name.ToCString()); | |
203 FileSystem* file_system = new FileSystem(key); | |
204 (*file_systems_)[key] = file_system; | |
205 | |
206 JSONObject jsobj(js); | |
207 jsobj.AddProperty("type", "Success"); | |
208 } | |
209 | |
210 | |
211 void DevFS::DeleteFileSystem(JSONStream* js, const String& fs_name) { | |
212 SafepointMutexLocker ml(mutex_); | |
213 FileSystem* file_system = LookupFileSystem(fs_name); | |
214 if (file_system == NULL) { | |
215 js->PrintError(kFileSystemDoesNotExist, | |
216 "%s: file system '%s' does not exist", | |
217 js->method(), fs_name.ToCString()); | |
218 return; | |
219 } | |
220 std::string key = std::string(fs_name.ToCString()); | |
221 file_systems_->erase(key); | |
222 delete file_system; | |
223 JSONObject jsobj(js); | |
224 jsobj.AddProperty("type", "Success"); | |
225 } | |
226 | |
227 | |
228 void DevFS::ListFiles(JSONStream* js, const String& fs_name) { | |
229 SafepointMutexLocker ml(mutex_); | |
230 FileSystem* file_system = LookupFileSystem(fs_name); | |
231 if (file_system == NULL) { | |
232 js->PrintError(kFileSystemDoesNotExist, | |
233 "%s: file system '%s' does not exist", | |
234 js->method(), fs_name.ToCString()); | |
235 return; | |
236 } | |
237 | |
238 file_system->ListFiles(js); | |
239 } | |
240 | |
241 | |
242 static void PrintWriteFilesError(JSONStream* js, | |
243 intptr_t i) { | |
244 js->PrintError(kInvalidParams, | |
245 "%s: files array invalid at index '%" Pd "'", | |
246 js->method(), i); | |
247 } | |
248 | |
249 | |
250 void DevFS::WriteFiles(JSONStream* js, | |
251 const String& fs_name, | |
252 const Array& files) { | |
253 SafepointMutexLocker ml(mutex_); | |
254 FileSystem* file_system = LookupFileSystem(fs_name); | |
255 if (file_system == NULL) { | |
256 js->PrintError(kFileSystemDoesNotExist, | |
257 "%s: file system '%s' does not exist", | |
258 js->method(), fs_name.ToCString()); | |
259 return; | |
260 } | |
261 | |
262 Object& test = Object::Handle(); | |
263 GrowableObjectArray& file_info = GrowableObjectArray::Handle(); | |
264 String& path = String::Handle(); | |
265 String& file_contents = String::Handle(); | |
266 | |
267 // First, validate the array of files is properly formed. | |
268 for (intptr_t i = 0; i < files.Length(); i++) { | |
269 test = files.At(i); | |
270 if (!test.IsGrowableObjectArray()) { | |
271 PrintWriteFilesError(js, i); | |
272 return; | |
273 } | |
274 file_info ^= test.raw(); | |
275 if (file_info.Length() != 2) { | |
276 PrintWriteFilesError(js, i); | |
277 return; | |
278 } | |
279 test = file_info.At(0); | |
280 if (!test.IsString()) { | |
281 PrintWriteFilesError(js, i); | |
282 return; | |
283 } | |
284 test = file_info.At(1); | |
285 if (!test.IsString()) { | |
286 PrintWriteFilesError(js, i); | |
287 return; | |
288 } | |
289 } | |
290 | |
291 // Now atomically update the file system. | |
292 for (intptr_t i = 0; i < files.Length(); i++) { | |
293 file_info = GrowableObjectArray::RawCast(files.At(i)); | |
294 path = String::RawCast(file_info.At(0)); | |
295 file_contents = String::RawCast(file_info.At(1)); | |
296 file_system->WriteFile(path.ToCString(), | |
297 file_contents.ToCString()); | |
298 } | |
299 | |
300 JSONObject jsobj(js); | |
301 jsobj.AddProperty("type", "Success"); | |
302 } | |
303 | |
304 | |
305 void DevFS::WriteFile(JSONStream* js, | |
306 const String& fs_name, | |
307 const String& path, | |
308 const String& file_contents) { | |
309 SafepointMutexLocker ml(mutex_); | |
310 FileSystem* file_system = LookupFileSystem(fs_name); | |
311 if (file_system == NULL) { | |
312 js->PrintError(kFileSystemDoesNotExist, | |
313 "%s: file system '%s' does not exist", | |
314 js->method(), fs_name.ToCString()); | |
315 return; | |
316 } | |
317 | |
318 file_system->WriteFile(path.ToCString(), | |
319 file_contents.ToCString()); | |
320 | |
321 JSONObject jsobj(js); | |
322 jsobj.AddProperty("type", "Success"); | |
323 } | |
324 | |
325 | |
326 void DevFS::WriteFileRaw(JSONStream* js, | |
327 const String& fs_name, | |
328 const String& path, | |
329 const String& file_contents) { | |
330 SafepointMutexLocker ml(mutex_); | |
331 FileSystem* file_system = LookupFileSystem(fs_name); | |
332 if (file_system == NULL) { | |
333 js->PrintError(kFileSystemDoesNotExist, | |
334 "%s: file system '%s' does not exist", | |
335 js->method(), fs_name.ToCString()); | |
336 return; | |
337 } | |
338 | |
339 file_system->WriteFile(path.ToCString(), | |
340 file_contents.ToCString()); | |
341 | |
342 JSONObject jsobj(js); | |
343 jsobj.AddProperty("type", "Success"); | |
344 } | |
345 | |
346 | |
347 void DevFS::ReadFile(JSONStream* js, | |
348 const String& fs_name, | |
349 const String& path) { | |
350 SafepointMutexLocker ml(mutex_); | |
351 FileSystem* file_system = LookupFileSystem(fs_name); | |
352 if (file_system == NULL) { | |
353 js->PrintError(kFileSystemDoesNotExist, | |
354 "%s: file system '%s' does not exist", | |
355 js->method(), fs_name.ToCString()); | |
356 return; | |
357 } | |
358 | |
359 std::string key = std::string(path.ToCString()); | |
360 std::vector<uint8_t>* file_contents; | |
361 | |
362 bool success = file_system->ReadFile(key, &file_contents); | |
363 | |
364 if (!success) { | |
365 js->PrintError(kFileDoesNotExist, | |
366 "%s: file 'dart-devfs://%s/%s' does not exist", | |
367 js->method(), fs_name.ToCString(), path.ToCString()); | |
368 return; | |
369 } | |
370 | |
371 JSONObject jsobj(js); | |
372 jsobj.AddProperty("type", "FSFile"); | |
373 jsobj.AddPropertyBase64("fileContents", | |
374 &((*file_contents)[0]), | |
375 file_contents->size()); | |
376 } | |
377 | |
378 Dart_Handle DevFS::ReadFile(const char* fs_name, | |
379 const char* path) { | |
380 SafepointMutexLocker ml(mutex_); | |
381 Thread* T = Thread::Current(); | |
382 FileSystem* file_system = LookupFileSystem(fs_name); | |
383 if (file_system == NULL) { | |
384 return Api::NewError("File system '%s' does not exist", fs_name); | |
385 } | |
386 | |
387 std::string key = std::string(path); | |
388 std::vector<uint8_t>* file_contents; | |
389 | |
390 bool success = file_system->ReadFile(key, &file_contents); | |
391 | |
392 if (!success) { | |
393 return Api::NewError("File '%s' does not exist", path); | |
394 } | |
395 | |
396 CHECK_CALLBACK_STATE(T); | |
397 | |
398 const TypedData& typed_data = | |
399 TypedData::Handle(TypedData::New(kTypedDataUint8ArrayCid, | |
400 file_contents->size())); | |
401 | |
402 { | |
403 NoSafepointScope scope; | |
404 uint8_t* destination = reinterpret_cast<uint8_t*>(typed_data.DataAddr(0)); | |
405 memmove(destination, &((*file_contents)[0]), file_contents->size()); | |
406 } | |
407 | |
408 return Api::NewHandle(T, typed_data.raw()); | |
409 } | |
410 | |
411 | |
412 Dart_Handle DevFS::ReadFileAsString(const char* fs_name, | |
413 const char* path) { | |
414 SafepointMutexLocker ml(mutex_); | |
415 Thread* T = Thread::Current(); | |
416 FileSystem* file_system = LookupFileSystem(fs_name); | |
417 if (file_system == NULL) { | |
418 return Api::NewError("File system '%s' does not exist", fs_name); | |
419 } | |
420 | |
421 std::string key = std::string(path); | |
422 std::vector<uint8_t>* file_contents; | |
423 | |
424 bool success = file_system->ReadFile(key, &file_contents); | |
425 | |
426 if (!success) { | |
427 return Api::NewError("File '%s' does not exist", path); | |
428 } | |
429 | |
430 uint8_t* data = &((*file_contents)[0]); | |
431 int64_t length = file_contents->size(); | |
432 CHECK_LENGTH(length, String::kMaxElements); | |
433 if (!Utf8::IsValid(&((*file_contents)[0]), file_contents->size())) { | |
434 return Api::NewError("File '%s' does not contain a valid UTF-8 string", | |
435 path); | |
436 } | |
437 CHECK_CALLBACK_STATE(T); | |
438 return Api::NewHandle(T, String::FromUTF8(data, length)); | |
439 } | |
440 | |
441 | |
442 Dart_Handle LibraryTagHandler(Dart_LibraryTag tag, | |
443 Dart_Handle library, | |
444 Dart_Handle url) { | |
445 if (tag == Dart_kCanonicalizeUrl) { | |
446 return Dart_DefaultCanonicalizeUrl(library, url); | |
447 } | |
448 | |
449 Dart_Handle source = Dart_DevFSReadFileAsUTF8String(url); | |
450 | |
451 if (Dart_IsError(source)) { | |
452 return source; | |
453 } | |
454 | |
455 // Lookup the library's url. | |
456 Dart_Handle library_url = Dart_LibraryUrl(library); | |
457 | |
458 switch (tag) { | |
459 case Dart_kImportTag: | |
460 return Dart_LoadLibrary(url, source, 0, 0); | |
461 break; | |
462 case Dart_kSourceTag: { | |
463 Dart_Handle library = Dart_LookupLibrary(library_url); | |
464 if (Dart_IsError(library)) { | |
465 return library; | |
466 } | |
467 return Dart_LoadSource(library, url, source, 0, 0); | |
468 } | |
469 break; | |
470 case Dart_kScriptTag: | |
471 return Dart_LoadScript(url, source, 0, 0); | |
472 break; | |
473 default: | |
474 UNREACHABLE(); | |
475 return Dart_Null(); | |
476 } | |
477 } | |
478 | |
479 } // namespace dart | |
OLD | NEW |