| Index: src/FileSystemJsObject.cpp |
| =================================================================== |
| new file mode 100644 |
| --- /dev/null |
| +++ b/src/FileSystemJsObject.cpp |
| @@ -0,0 +1,411 @@ |
| +#include <AdblockPlus/FileSystem.h> |
| +#include <stdexcept> |
| +#include <sstream> |
| +#include <vector> |
| + |
| +#include "FileSystemJsObject.h" |
| +#include "Utils.h" |
| +#include "Thread.h" |
| + |
| +using namespace AdblockPlus; |
| + |
| +namespace |
| +{ |
| + class IoThread : public Thread |
| + { |
| + public: |
| + IoThread(FileSystem& fileSystem, v8::Persistent<v8::Function> callback) |
|
Wladimir Palant
2013/04/12 16:10:35
Passing in callback like this is rather ugly - you
|
| + : isolate(v8::Isolate::GetCurrent()), |
| + context(v8::Persistent<v8::Context>::New(isolate, |
| + v8::Context::GetCurrent())), |
| + fileSystem(fileSystem), callback(callback) |
| + { |
| + } |
| + |
| + virtual ~IoThread() |
| + { |
| + callback.Dispose(isolate); |
| + context.Dispose(isolate); |
| + } |
| + |
| + protected: |
| + v8::Isolate* const isolate; |
| + v8::Persistent<v8::Context> context; |
| + FileSystem& fileSystem; |
| + v8::Persistent<v8::Function> callback; |
| + }; |
| + |
| + class ReadThread : public IoThread |
| + { |
| + public: |
| + ReadThread(FileSystem& fileSystem, v8::Persistent<v8::Function> callback, |
| + const std::string& path) |
| + : IoThread(fileSystem, callback), path(path) |
| + { |
| + } |
| + |
| + void Run() |
| + { |
| + std::string content; |
| + std::string error; |
| + try |
| + { |
| + std::auto_ptr<std::istream> stream = fileSystem.Read(path); |
| + content = Utils::Slurp(*stream); |
| + } |
| + catch (std::exception& e) |
| + { |
| + error = e.what(); |
| + } |
| + catch (...) |
| + { |
| + error = "Unknown error while reading from " + path; |
| + } |
| + |
| + const v8::Locker locker(isolate); |
| + const v8::HandleScope handleScope; |
| + const v8::Context::Scope contextScope(context); |
| + v8::Handle<v8::Object> result = v8::Object::New(); |
| + result->Set(v8::String::New("content"), v8::String::New(content.c_str())); |
| + result->Set(v8::String::New("error"), v8::String::New(error.c_str())); |
|
Wladimir Palant
2013/04/12 16:10:35
Please always use the string length when convertin
Felix Dahlke
2013/04/15 03:43:34
Will do, I meant to add string conversion function
|
| + callback->Call(callback, 1, |
| + reinterpret_cast<v8::Handle<v8::Value>*>(&result)); |
| + delete this; |
| + } |
| + |
| + private: |
| + std::string path; |
| + }; |
| + |
| + class WriteThread : public IoThread |
| + { |
| + public: |
| + WriteThread(FileSystem& fileSystem, v8::Persistent<v8::Function> callback, |
| + const std::string& path, const std::string& content) |
| + : IoThread(fileSystem, callback), path(path), content(content) |
| + { |
| + } |
| + |
| + void Run() |
| + { |
| + std::string error; |
| + try |
| + { |
| + fileSystem.Write(path, content); |
| + } |
| + catch (std::exception& e) |
| + { |
| + error = e.what(); |
| + } |
| + catch (...) |
| + { |
| + error = "Unknown error while writing to " + path; |
| + } |
| + |
| + const v8::Locker locker(isolate); |
| + const v8::HandleScope handleScope; |
| + const v8::Context::Scope contextScope(context); |
| + v8::Handle<v8::Value> errorValue = v8::String::New(error.c_str()); |
| + callback->Call(callback, 1, &errorValue); |
| + delete this; |
| + } |
| + |
| + private: |
| + std::string path; |
| + std::string content; |
| + }; |
| + |
| + class MoveThread : public IoThread |
| + { |
| + public: |
| + MoveThread(FileSystem& fileSystem, v8::Persistent<v8::Function> callback, |
| + const std::string& fromPath, const std::string& toPath) |
| + : IoThread(fileSystem, callback), fromPath(fromPath), toPath(toPath) |
| + { |
| + } |
| + |
| + void Run() |
| + { |
| + std::string error; |
| + try |
| + { |
| + fileSystem.Move(fromPath, toPath); |
| + } |
| + catch (std::exception& e) |
| + { |
| + error = e.what(); |
| + } |
| + catch (...) |
| + { |
| + error = "Unknown error while moving " + fromPath + " to " + toPath; |
| + } |
| + |
| + const v8::Locker locker(isolate); |
| + const v8::HandleScope handleScope; |
| + const v8::Context::Scope contextScope(context); |
| + v8::Handle<v8::Value> errorValue = v8::String::New(error.c_str()); |
| + callback->Call(callback, 1, &errorValue); |
| + delete this; |
| + } |
| + |
| + private: |
| + std::string fromPath; |
| + std::string toPath; |
| + }; |
| + |
| + class RemoveThread : public IoThread |
| + { |
| + public: |
| + RemoveThread(FileSystem& fileSystem, v8::Persistent<v8::Function> callback, |
| + const std::string& path) |
| + : IoThread(fileSystem, callback), path(path) |
| + { |
| + } |
| + |
| + void Run() |
| + { |
| + std::string error; |
| + try |
| + { |
| + fileSystem.Remove(path); |
| + } |
| + catch (std::exception& e) |
| + { |
| + error = e.what(); |
| + } |
| + catch (...) |
| + { |
| + error = "Unknown error while removing " + path; |
| + } |
| + |
| + const v8::Locker locker(isolate); |
| + const v8::HandleScope handleScope; |
| + const v8::Context::Scope contextScope(context); |
| + v8::Handle<v8::Value> errorValue = v8::String::New(error.c_str()); |
| + callback->Call(callback, 1, &errorValue); |
| + delete this; |
| + } |
| + |
| + private: |
| + std::string path; |
| + }; |
| + |
| + class StatThread : public IoThread |
| + { |
| + public: |
| + StatThread(FileSystem& fileSystem, v8::Persistent<v8::Function> callback, |
| + const std::string& path) |
| + : IoThread(fileSystem, callback), path(path) |
| + { |
| + } |
| + |
| + void Run() |
| + { |
| + std::string error; |
| + FileSystem::StatResult statResult; |
| + try |
| + { |
| + statResult = fileSystem.Stat(path); |
| + } |
| + catch (std::exception& e) |
| + { |
| + error = e.what(); |
| + } |
| + catch (...) |
| + { |
| + error = "Unknown error while calling stat on " + path; |
| + } |
| + |
| + const v8::Locker locker(isolate); |
| + const v8::HandleScope handleScope; |
| + const v8::Context::Scope contextScope(context); |
| + v8::Handle<v8::Object> result = v8::Object::New(); |
| + result->Set(v8::String::New("exists"), |
| + v8::Boolean::New(statResult.exists)); |
| + result->Set(v8::String::New("isFile"), |
| + v8::Boolean::New(statResult.isFile)); |
| + result->Set(v8::String::New("isDirectory"), |
| + v8::Boolean::New(statResult.isDirectory)); |
| + result->Set(v8::String::New("lastModified"), |
| + v8::Number::New(statResult.lastModified)); |
| + result->Set(v8::String::New("error"), v8::String::New(error.c_str())); |
| + callback->Call(callback, 1, |
| + reinterpret_cast<v8::Handle<v8::Value>*>(&result)); |
| + delete this; |
| + } |
| + |
| + private: |
| + std::string path; |
| + }; |
| + |
| + std::string CheckArguments(const v8::Arguments& arguments, |
| + const std::string& functionName, |
|
Wladimir Palant
2013/04/12 16:10:35
Will arguments.Callee()->GetName() do as well?
|
| + const std::vector<std::string> requiredTypes) |
| + { |
| + const int requiredCount = requiredTypes.size(); |
| + if (arguments.Length() != requiredCount) |
| + { |
| + std::stringstream stream; |
| + stream << functionName << " requires " << requiredCount |
| + << (requiredCount == 1 ? " parameter" : " parameters"); |
| + return stream.str(); |
| + } |
| + |
| + for (int i = 0; i < requiredCount; i++) |
| + { |
| + const v8::Handle<v8::Value> argument = arguments[i]; |
| + const std::string requiredType = requiredTypes[i]; |
| + if ((requiredType == "string" && !argument->IsString()) |
| + || (requiredType == "number" && !argument->IsNumber()) |
|
Wladimir Palant
2013/04/12 16:10:35
Please note that a parameter doesn't need to be a
|
| + || (requiredType == "function" && !argument->IsFunction())) |
| + { |
| + std::vector<std::string> countWords; |
| + countWords.push_back("First"); |
| + countWords.push_back("Second"); |
| + countWords.push_back("Third"); |
| + std::stringstream stream; |
| + if (i < countWords.size()) |
| + stream << countWords[i] << " argument"; |
| + else |
| + stream << "Argument " << i; |
|
Wladimir Palant
2013/04/12 16:10:35
I would opt against unnecessary complexity - pleas
|
| + stream << " to " << functionName << " must be a " << requiredType; |
| + return stream.str(); |
| + } |
| + } |
| + |
| + return ""; |
| + } |
| + |
| + v8::Handle<v8::Value> ReadCallback(const v8::Arguments& arguments) |
| + { |
| + std::vector<std::string> requiredTypes; |
| + requiredTypes.push_back("string"); |
| + requiredTypes.push_back("function"); |
| + const std::string error = CheckArguments(arguments, "_fileSystem.read", |
| + requiredTypes); |
|
Wladimir Palant
2013/04/12 16:10:35
Mozilla opted for a more trivial CheckArguments("s
Felix Dahlke
2013/04/15 03:43:34
I mainly introduced this to get the duplication do
|
| + if (error.length()) |
| + return v8::ThrowException(v8::String::New(error.c_str())); |
| + |
| + const v8::Handle<const v8::External> external = |
| + v8::Handle<const v8::External>::Cast(arguments.Data()); |
| + FileSystem* const fileSystem = static_cast<FileSystem*>(external->Value()); |
| + const std::string path = *v8::String::Utf8Value(arguments[0]->ToString()); |
| + v8::Persistent<v8::Function> callback = v8::Persistent<v8::Function>::New( |
| + v8::Isolate::GetCurrent(), v8::Handle<v8::Function>::Cast(arguments[1])); |
| + ReadThread* const readThread = new ReadThread(*fileSystem, callback, path); |
| + readThread->Start(); |
| + return v8::Undefined(); |
| + } |
| + |
| + v8::Handle<v8::Value> WriteCallback(const v8::Arguments& arguments) |
| + { |
| + std::vector<std::string> requiredTypes; |
| + requiredTypes.push_back("string"); |
| + requiredTypes.push_back("string"); |
| + requiredTypes.push_back("function"); |
| + const std::string error = CheckArguments(arguments, "_fileSystem.write", |
| + requiredTypes); |
| + if (error.length()) |
| + return v8::ThrowException(v8::String::New(error.c_str())); |
| + |
| + const v8::Handle<const v8::External> external = |
| + v8::Handle<const v8::External>::Cast(arguments.Data()); |
| + FileSystem* const fileSystem = static_cast<FileSystem*>(external->Value()); |
| + const std::string path = *v8::String::Utf8Value(arguments[0]->ToString()); |
| + const std::string content = |
| + *v8::String::Utf8Value(arguments[1]->ToString()); |
| + v8::Persistent<v8::Function> callback = v8::Persistent<v8::Function>::New( |
| + v8::Isolate::GetCurrent(), v8::Handle<v8::Function>::Cast(arguments[2])); |
| + WriteThread* const writeThread = new WriteThread(*fileSystem, callback, |
| + path, content); |
|
Wladimir Palant
2013/04/12 16:10:35
Just a thought: with the content parameter being a
|
| + writeThread->Start(); |
| + return v8::Undefined(); |
| + } |
| + |
| + v8::Handle<v8::Value> MoveCallback(const v8::Arguments& arguments) |
| + { |
| + std::vector<std::string> requiredTypes; |
| + requiredTypes.push_back("string"); |
| + requiredTypes.push_back("string"); |
| + requiredTypes.push_back("function"); |
| + const std::string error = CheckArguments(arguments, "_fileSystem.move", |
| + requiredTypes); |
| + if (error.length()) |
| + return v8::ThrowException(v8::String::New(error.c_str())); |
| + |
| + const v8::Handle<const v8::External> external = |
| + v8::Handle<const v8::External>::Cast(arguments.Data()); |
| + FileSystem* const fileSystem = static_cast<FileSystem*>(external->Value()); |
| + const std::string fromPath = |
| + *v8::String::Utf8Value(arguments[0]->ToString()); |
| + const std::string toPath = *v8::String::Utf8Value(arguments[1]->ToString()); |
| + v8::Persistent<v8::Function> callback = v8::Persistent<v8::Function>::New( |
| + v8::Isolate::GetCurrent(), v8::Handle<v8::Function>::Cast(arguments[2])); |
| + MoveThread* const moveThread = new MoveThread(*fileSystem, callback, |
| + fromPath, toPath); |
| + moveThread->Start(); |
| + return v8::Undefined(); |
| + } |
| + |
| + v8::Handle<v8::Value> RemoveCallback(const v8::Arguments& arguments) |
| + { |
| + std::vector<std::string> requiredTypes; |
| + requiredTypes.push_back("string"); |
| + requiredTypes.push_back("function"); |
| + const std::string error = CheckArguments(arguments, "_fileSystem.remove", |
| + requiredTypes); |
| + if (error.length()) |
| + return v8::ThrowException(v8::String::New(error.c_str())); |
| + |
| + const v8::Handle<const v8::External> external = |
| + v8::Handle<const v8::External>::Cast(arguments.Data()); |
| + FileSystem* const fileSystem = static_cast<FileSystem*>(external->Value()); |
| + const std::string path = *v8::String::Utf8Value(arguments[0]->ToString()); |
| + v8::Persistent<v8::Function> callback = v8::Persistent<v8::Function>::New( |
| + v8::Isolate::GetCurrent(), v8::Handle<v8::Function>::Cast(arguments[1])); |
| + RemoveThread* const removeThread = new RemoveThread(*fileSystem, callback, |
| + path); |
| + removeThread->Start(); |
| + return v8::Undefined(); |
| + } |
| + |
| + v8::Handle<v8::Value> StatCallback(const v8::Arguments& arguments) |
| + { |
| + std::vector<std::string> requiredTypes; |
| + requiredTypes.push_back("string"); |
| + requiredTypes.push_back("function"); |
| + const std::string error = CheckArguments(arguments, "_fileSystem.stat", |
| + requiredTypes); |
| + if (error.length()) |
| + return v8::ThrowException(v8::String::New(error.c_str())); |
| + |
| + const v8::Handle<const v8::External> external = |
| + v8::Handle<const v8::External>::Cast(arguments.Data()); |
| + FileSystem* const fileSystem = static_cast<FileSystem*>(external->Value()); |
| + const std::string path = *v8::String::Utf8Value(arguments[0]->ToString()); |
| + v8::Persistent<v8::Function> callback = v8::Persistent<v8::Function>::New( |
| + v8::Isolate::GetCurrent(), v8::Handle<v8::Function>::Cast(arguments[1])); |
| + StatThread* const statThread = new StatThread(*fileSystem, callback, path); |
| + statThread->Start(); |
| + return v8::Undefined(); |
| + } |
| +} |
| + |
| +v8::Handle<v8::ObjectTemplate> |
| +FileSystemJsObject::Create(FileSystem& fileSystem) |
| +{ |
| + const v8::Locker locker(v8::Isolate::GetCurrent()); |
| + v8::HandleScope handleScope; |
| + const v8::Handle<v8::ObjectTemplate> file = v8::ObjectTemplate::New(); |
| + file->Set(v8::String::New("read"), |
| + v8::FunctionTemplate::New(ReadCallback, v8::External::New(&fileSystem))); |
|
Wladimir Palant
2013/04/12 16:10:35
How about using the same v8::External instance for
|
| + file->Set(v8::String::New("write"), |
| + v8::FunctionTemplate::New(WriteCallback, v8::External::New(&fileSystem))); |
| + file->Set(v8::String::New("move"), |
| + v8::FunctionTemplate::New(MoveCallback, v8::External::New(&fileSystem))); |
| + file->Set(v8::String::New("remove"), |
| + v8::FunctionTemplate::New(RemoveCallback, v8::External::New(&fileSystem))); |
| + file->Set(v8::String::New("stat"), |
| + v8::FunctionTemplate::New(StatCallback, v8::External::New(&fileSystem))); |
| + return handleScope.Close(file); |
| +} |