From 308124c99f0e0480430cfc049cf703721cc320ff Mon Sep 17 00:00:00 2001 From: faraphel Date: Mon, 12 Aug 2024 13:55:33 +0200 Subject: [PATCH] base for the Atlas JavaScript engine : implemented the Engine, a virtual filesytem and prepared the library system. --- .gitignore | 199 +----------------- .gitmodules | 6 +- .mods/Atlas | 1 + CMakeLists.txt | 50 +++++ external/rvfs/CMakeLists.txt | 27 +++ external/rvfs/README.md | 10 + .../include/RestrictedVirtualFileSystem.hpp | 62 ++++++ .../source/RestrictedVirtualFileSystem.cpp | 65 ++++++ external/rvfs/tests/CMakeLists.txt | 36 ++++ external/rvfs/tests/_assets/content.txt | 1 + external/rvfs/tests/test-restriction.cpp | 37 ++++ external/szs | 1 + external/wit | 1 + source/javascript/engine/AtlasJsEngine.cpp | 20 ++ source/javascript/engine/AtlasJsEngine.hpp | 24 +++ source/javascript/module/AtlasJsModule.cpp | 47 +++++ source/javascript/module/AtlasJsModule.hpp | 34 +++ .../javascript/module/_base/BaseJsModule.cpp | 6 + .../javascript/module/_base/BaseJsModule.hpp | 19 ++ .../javascript/module/debug/DebugJsModule.cpp | 10 + .../javascript/module/debug/DebugJsModule.hpp | 19 ++ .../javascript/module/file/FileJsModule.cpp | 12 ++ .../javascript/module/file/FileJsModule.hpp | 18 ++ .../javascript/module/image/ImageJsModule.cpp | 11 + .../javascript/module/image/ImageJsModule.hpp | 23 ++ .../javascript/module/image/ImageJsObject.cpp | 20 ++ .../javascript/module/image/ImageJsObject.hpp | 22 ++ source/main.cpp | 71 +++++++ 28 files changed, 655 insertions(+), 197 deletions(-) create mode 160000 .mods/Atlas create mode 100644 CMakeLists.txt create mode 100644 external/rvfs/CMakeLists.txt create mode 100644 external/rvfs/README.md create mode 100644 external/rvfs/include/RestrictedVirtualFileSystem.hpp create mode 100644 external/rvfs/source/RestrictedVirtualFileSystem.cpp create mode 100644 external/rvfs/tests/CMakeLists.txt create mode 100644 external/rvfs/tests/_assets/content.txt create mode 100644 external/rvfs/tests/test-restriction.cpp create mode 160000 external/szs create mode 160000 external/wit create mode 100644 source/javascript/engine/AtlasJsEngine.cpp create mode 100644 source/javascript/engine/AtlasJsEngine.hpp create mode 100644 source/javascript/module/AtlasJsModule.cpp create mode 100644 source/javascript/module/AtlasJsModule.hpp create mode 100644 source/javascript/module/_base/BaseJsModule.cpp create mode 100644 source/javascript/module/_base/BaseJsModule.hpp create mode 100644 source/javascript/module/debug/DebugJsModule.cpp create mode 100644 source/javascript/module/debug/DebugJsModule.hpp create mode 100644 source/javascript/module/file/FileJsModule.cpp create mode 100644 source/javascript/module/file/FileJsModule.hpp create mode 100644 source/javascript/module/image/ImageJsModule.cpp create mode 100644 source/javascript/module/image/ImageJsModule.hpp create mode 100644 source/javascript/module/image/ImageJsObject.cpp create mode 100644 source/javascript/module/image/ImageJsObject.hpp create mode 100644 source/main.cpp diff --git a/.gitignore b/.gitignore index 68751ca..b00a62c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,196 +1,7 @@ -# ---> Python -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class +# IDE +.idea/ +.venv/ -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -# ---> C++ -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app +# CMake +cmake-*/ diff --git a/.gitmodules b/.gitmodules index abcd2da..00850b9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,9 @@ [submodule ".mods/Atlas"] path = .mods/Atlas url = https://git.faraphel.fr/Atlas/Atlas-Mod -[submodule "library-szs"] +[submodule "external/szs"] path = external/szs url = https://git.faraphel.fr/Atlas/library-szs -[submodule "library-wit"] - path = external/szs +[submodule "external/wit"] + path = external/wit url = https://git.faraphel.fr/Atlas/library-wit diff --git a/.mods/Atlas b/.mods/Atlas new file mode 160000 index 0000000..997d361 --- /dev/null +++ b/.mods/Atlas @@ -0,0 +1 @@ +Subproject commit 997d361d05a2f022d027118f6d1cb5c9642586a5 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f29eb3e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.28) +project(Atlas-Launcher LANGUAGES CXX) + + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + + +find_package(Qt6 REQUIRED COMPONENTS + Core + Gui + Widgets + Qml +) +add_executable(Atlas-Launcher + source/main.cpp + source/javascript/module/AtlasJsModule.cpp + source/javascript/module/AtlasJsModule.hpp + source/javascript/engine/AtlasJsEngine.cpp + source/javascript/engine/AtlasJsEngine.hpp + source/javascript/module/debug/DebugJsModule.cpp + source/javascript/module/debug/DebugJsModule.hpp + source/javascript/module/image/ImageJsModule.cpp + source/javascript/module/image/ImageJsModule.hpp + source/javascript/module/image/ImageJsObject.cpp + source/javascript/module/image/ImageJsObject.hpp + source/javascript/module/_base/BaseJsModule.cpp + source/javascript/module/_base/BaseJsModule.hpp + source/javascript/module/file/FileJsModule.cpp + source/javascript/module/file/FileJsModule.hpp +) +target_link_libraries(Atlas-Launcher PRIVATE + # Tools + RVFS + SZS + + # Qt Framework + Qt::Core + Qt::Gui + Qt::Widgets + Qt::Qml +) + + +add_subdirectory(external/rvfs) +add_subdirectory(external/szs) diff --git a/external/rvfs/CMakeLists.txt b/external/rvfs/CMakeLists.txt new file mode 100644 index 0000000..ff4ada6 --- /dev/null +++ b/external/rvfs/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.28) +project(RVFS LANGUAGES CXX) + + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + + +find_package(Qt6 REQUIRED COMPONENTS + Core +) +add_library(RVFS STATIC + source/RestrictedVirtualFileSystem.cpp +) +target_include_directories(RVFS PUBLIC + include/ +) +target_link_libraries(RVFS PRIVATE + # Qt Framework + Qt::Core +) + +add_subdirectory(tests) \ No newline at end of file diff --git a/external/rvfs/README.md b/external/rvfs/README.md new file mode 100644 index 0000000..8c0106e --- /dev/null +++ b/external/rvfs/README.md @@ -0,0 +1,10 @@ +# Restricted Virtual File System +This is a micro-library used to create a virtual file system to restrict access +to the host file system while still being able to mount directories that can be accessed. + +As of right now, this is an incredibly simple implementation of this system that +work by mapping virtual directory path to a corresponding real directory path, replacing +them while resolving the path and reject all others paths that have not been mounted +to avoid access to other files. + +It does not emulate a real file system nor allow control over permissions of the files. diff --git a/external/rvfs/include/RestrictedVirtualFileSystem.hpp b/external/rvfs/include/RestrictedVirtualFileSystem.hpp new file mode 100644 index 0000000..3a572df --- /dev/null +++ b/external/rvfs/include/RestrictedVirtualFileSystem.hpp @@ -0,0 +1,62 @@ +#pragma once +#include +#include + + +namespace rvfs { + +/** + * Represent a virtual restricted file system. + * Real directory can be mounted to safely interact with the real file system. + */ +class RestrictedVirtualFileSystem { + +public: + explicit RestrictedVirtualFileSystem(); + + /** + * Get all the mounted directories with their path in the virtual file system and in the real file system + * @return all the mounted directories with their path in the virtual file system and in the real file system + */ + QMap getMountedDirectories(); + + /** + * Mount a real directory into the virtual file system + * @param destination the path to the directory in the virtual file system + * @param source the path to the directory in the real file system + */ + void mountDirectory(const QDir& destination, const QDir& source); + + /** + * Unmount a directory from the virtual file system + * @param destination the path to the directory in the virtual file system + */ + void unmountDirectory(const QDir& destination); + + /** + * Get the current working directory in the virtual file system + * @return the current working directory in the virtual file system + */ + QDir getWorkingDirectory(); + /** + * Change the current working directory in the virtual file system + * @param path path to the new virtual working directory + */ + void setWorkingDirectory(const QDir& path); + + /** + * Resolve a path inside the virtual file system into a path in the real file system + * @param path the path in the virtual file system + * @return the matching path in the real file system + */ + QString resolvePath(QString path); + +private: + /// the current working directory + QDir workingDirectory; + /// store the path in the virtual file system of a directory in the real file system + QMap mounts; + +}; + +} \ No newline at end of file diff --git a/external/rvfs/source/RestrictedVirtualFileSystem.cpp b/external/rvfs/source/RestrictedVirtualFileSystem.cpp new file mode 100644 index 0000000..59da494 --- /dev/null +++ b/external/rvfs/source/RestrictedVirtualFileSystem.cpp @@ -0,0 +1,65 @@ +#include "RestrictedVirtualFileSystem.hpp" + + +namespace rvfs { + +RestrictedVirtualFileSystem::RestrictedVirtualFileSystem() = default; + +QMap RestrictedVirtualFileSystem::getMountedDirectories() { + return this->mounts; +} + +void RestrictedVirtualFileSystem::mountDirectory(const QDir& destination, const QDir& source) { + // check if the source directory does exists + if (!source.exists()) + // TODO(Faraphel): should be a custom error ? + throw std::runtime_error("The source directory does not exists."); + + // mount it by associating our virtual folder with our real one + this->mounts[destination.absolutePath()] = source.absolutePath(); +} + +void RestrictedVirtualFileSystem::unmountDirectory(const QDir& destination) { + // remove the mounted directory from the map + this->mounts.remove(destination.absolutePath()); +} + +QDir RestrictedVirtualFileSystem::getWorkingDirectory() { + return this->workingDirectory; +} + +void RestrictedVirtualFileSystem::setWorkingDirectory(const QDir& path) { + this->workingDirectory = path; +} + +QString RestrictedVirtualFileSystem::resolvePath(QString path) { + if (QFileInfo(path).isRelative()) { + // if the path is relative, make it absolute with the current working directory + path = this->workingDirectory.absoluteFilePath(path); + } else { + // if the path is absolute, make sure to resolve the special syntax like ".." + path = QFileInfo(path).absoluteFilePath(); + } + + bool resolved = false; + // go through all the mounted directories + for (auto const& [destination, source] : this->mounts.asKeyValueRange()) { + // if the path is inside a mounted directory + if (path.startsWith(destination)) { + // replace the mounted directory by the original real one + path = path.replace(0, destination.length(), source); + // mark the path as resolved and break + resolved = true; + break; + } + } + + // if the path has not been resolved, it is in an invalid or forbidden location + if (!resolved) + // TODO(Faraphel): should be a custom error ? + throw std::runtime_error("cannot resolve path: invalid."); + + return path; +} + +} \ No newline at end of file diff --git a/external/rvfs/tests/CMakeLists.txt b/external/rvfs/tests/CMakeLists.txt new file mode 100644 index 0000000..f68273f --- /dev/null +++ b/external/rvfs/tests/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.28) +project(Test-RVFS LANGUAGES CXX) + + +enable_testing() + + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + + +# Packages +find_package(Qt6 REQUIRED COMPONENTS + Core +) + +# Test executable +add_executable(Test-RVFS + test-restriction.cpp +) +# Libraries +target_link_libraries(Test-RVFS PRIVATE + # Code to test + RVFS + # Qt Framework + Qt::Core +) +# Copy the assets to run the tests +file(COPY _assets DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + +# Test +add_test(NAME Test-RVFS COMMAND Test-RVFS) diff --git a/external/rvfs/tests/_assets/content.txt b/external/rvfs/tests/_assets/content.txt new file mode 100644 index 0000000..8a03e0e --- /dev/null +++ b/external/rvfs/tests/_assets/content.txt @@ -0,0 +1 @@ +This is a simple text file. diff --git a/external/rvfs/tests/test-restriction.cpp b/external/rvfs/tests/test-restriction.cpp new file mode 100644 index 0000000..231c28b --- /dev/null +++ b/external/rvfs/tests/test-restriction.cpp @@ -0,0 +1,37 @@ +#include +#include + +int main() { + // TODO(Faraphel): use a real unit test framework ? + + // create a basic rvfs + rvfs::RestrictedVirtualFileSystem fs; + + // mount a directory into our system + fs.mountDirectory(QDir("/assets/"), QDir("./_assets/")); + + // TEST: check if a file outside of an allowed mounted directory can't be resolved + try { + const QString& path = fs.resolvePath("/content.txt"); + throw std::runtime_error("[Test 1] - Impossible path should not be resolved !"); + } catch (const std::runtime_error& exception) {} + + // TEST: check if a file in a mounted directory can be resolved + try { + const QString& path = fs.resolvePath("/assets/content.txt"); + + auto file = QFile(path); + file.open(QIODeviceBase::Text | QIODevice::ReadOnly); + std::cout << file.readAll().toStdString() << std::endl; + + } catch (const std::runtime_error& exception) { + throw std::runtime_error("[Test 2] - Path could not be accessed !"); + } + + // TEST: cannot bypass security with the ".." parent directory syntax + try { + const QString& path = fs.resolvePath("/assets/../content.txt"); + throw std::runtime_error("[Test 3] - Resolved impossible path"); + } catch (const std::runtime_error& exception) { + } +} diff --git a/external/szs b/external/szs new file mode 160000 index 0000000..3b292fb --- /dev/null +++ b/external/szs @@ -0,0 +1 @@ +Subproject commit 3b292fbcad336ebb2d9137a379c8a9cdaf945162 diff --git a/external/wit b/external/wit new file mode 160000 index 0000000..8032849 --- /dev/null +++ b/external/wit @@ -0,0 +1 @@ +Subproject commit 80328498a1f2446a67a1df55900edb63e6e93bb0 diff --git a/source/javascript/engine/AtlasJsEngine.cpp b/source/javascript/engine/AtlasJsEngine.cpp new file mode 100644 index 0000000..ea83999 --- /dev/null +++ b/source/javascript/engine/AtlasJsEngine.cpp @@ -0,0 +1,20 @@ +#include "AtlasJsEngine.hpp" + +#include "../module/AtlasJsModule.hpp" + + +namespace atlas::js { + + +AtlasJsEngine::AtlasJsEngine(QObject *parent) : QJSEngine(parent) { + // instanciate a new Atlas module + this->moduleAtlas = std::make_unique(this); + + // convert it into a javascript object + const QJSValue jsModuleAtlas = this->newQObject(this->moduleAtlas.get()); + // make it accessible as "atlas" in the javascript code + this->globalObject().setProperty("atlas", jsModuleAtlas); +} + + +} diff --git a/source/javascript/engine/AtlasJsEngine.hpp b/source/javascript/engine/AtlasJsEngine.hpp new file mode 100644 index 0000000..df9fe1f --- /dev/null +++ b/source/javascript/engine/AtlasJsEngine.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include + +#include "../module/AtlasJsModule.hpp" + + +namespace atlas::js { + +/** + * This class represent a Qt JavaScript engine modified to support the Atlas framework. + */ +class AtlasJsEngine : public QJSEngine { + +public: + explicit AtlasJsEngine(QObject* parent); + +private: + std::shared_ptr moduleAtlas; +}; + +} diff --git a/source/javascript/module/AtlasJsModule.cpp b/source/javascript/module/AtlasJsModule.cpp new file mode 100644 index 0000000..ea4e8ec --- /dev/null +++ b/source/javascript/module/AtlasJsModule.cpp @@ -0,0 +1,47 @@ +#include "AtlasJsModule.hpp" + +#include + +#include "debug/DebugJsModule.hpp" +#include "image/ImageJsModule.hpp" + + +AtlasJsModule::AtlasJsModule(QJSEngine* engine, QObject* parent) : QObject(parent) { + // set the engine + this->engine = engine; + + // load the submodules + this->submodules[QStringLiteral("debug")] = std::make_shared(this->engine); + this->submodules[QStringLiteral("image")] = std::make_shared(this->engine); +} + +QJSValue AtlasJsModule::require(const QString& name) { + std::shared_ptr submodule; + + try { + // get the submodule from its name + submodule = this->submodules.at(name); + } catch (const std::out_of_range&) { + // if not found, throw a Javascript error and return null + this->engine->throwError( + QJSValue::ReferenceError, + QStringLiteral("the module \"") + name + QStringLiteral("\" does not exist.") + ); + return QJSValue(QJSPrimitiveNull()); + } + + // return the module as a new javascript object + return this->engine->newQObject(submodule.get()); +} + +QJSValueList AtlasJsModule::getVersion() { + // TODO(Faraphel): should be stored somewhere else. + // TODO(Faraphel): The type should implement an easy comparison system, if possible + QJSValueList version; + + version.append(1); + version.append(0); + version.append(0); + + return version; +} \ No newline at end of file diff --git a/source/javascript/module/AtlasJsModule.hpp b/source/javascript/module/AtlasJsModule.hpp new file mode 100644 index 0000000..12c0a30 --- /dev/null +++ b/source/javascript/module/AtlasJsModule.hpp @@ -0,0 +1,34 @@ +#pragma once + + +#include +#include + +#include "_base/BaseJsModule.hpp" + + +class AtlasJsModule : public QObject { + Q_OBJECT + +public: + explicit AtlasJsModule(QJSEngine* engine, QObject* parent = nullptr); + + /** + * Import an Atlas submodule + * @param name the name of the module to import + * @return the module + */ + Q_INVOKABLE QJSValue require(const QString& name); + + /** + * Get the version of the Atlas Javascript engine + * @return the Atlas version + */ + Q_INVOKABLE static QJSValueList getVersion(); + +private: + /// the parent JavaScript engine + QJSEngine* engine; + /// the submodules contained in this module + std::map> submodules; +}; diff --git a/source/javascript/module/_base/BaseJsModule.cpp b/source/javascript/module/_base/BaseJsModule.cpp new file mode 100644 index 0000000..f8b103f --- /dev/null +++ b/source/javascript/module/_base/BaseJsModule.cpp @@ -0,0 +1,6 @@ +#include "BaseJsModule.hpp" + + +BaseJsModule::BaseJsModule(QJSEngine *engine) { + this->engine = engine; +} diff --git a/source/javascript/module/_base/BaseJsModule.hpp b/source/javascript/module/_base/BaseJsModule.hpp new file mode 100644 index 0000000..d5c5a68 --- /dev/null +++ b/source/javascript/module/_base/BaseJsModule.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + + +/** + * Base class for an Atlas JavaScript submodule + */ +class BaseJsModule : public QObject { + Q_OBJECT + +public: + explicit BaseJsModule(QJSEngine* engine); + +protected: + /// The javascript engine running the module + QJSEngine* engine; +}; diff --git a/source/javascript/module/debug/DebugJsModule.cpp b/source/javascript/module/debug/DebugJsModule.cpp new file mode 100644 index 0000000..5459fc6 --- /dev/null +++ b/source/javascript/module/debug/DebugJsModule.cpp @@ -0,0 +1,10 @@ +#include "DebugJsModule.hpp" + +#include + + +DebugJsModule::DebugJsModule(QJSEngine* engine) : BaseJsModule(engine) {} + +void DebugJsModule::information(const QString& text) { + qDebug() << text; +} diff --git a/source/javascript/module/debug/DebugJsModule.hpp b/source/javascript/module/debug/DebugJsModule.hpp new file mode 100644 index 0000000..ca451be --- /dev/null +++ b/source/javascript/module/debug/DebugJsModule.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "../_base/BaseJsModule.hpp" + + +/** + * This module implements useful feature to help with debugging + */ +class DebugJsModule : public BaseJsModule { + Q_OBJECT + +public: + explicit DebugJsModule(QJSEngine* engine); + + /** + * Print an informational message + */ + Q_INVOKABLE static void information(const QString& text); +}; diff --git a/source/javascript/module/file/FileJsModule.cpp b/source/javascript/module/file/FileJsModule.cpp new file mode 100644 index 0000000..9ad57a1 --- /dev/null +++ b/source/javascript/module/file/FileJsModule.cpp @@ -0,0 +1,12 @@ +#include "FileJsModule.hpp" + +#include + + +FileJsModule::FileJsModule(QJSEngine *engine, const QList& allowedDirectories) : BaseJsModule(engine) { + this->allowedDirectories = allowedDirectories; +} + +QJSValue FileJsModule::open(const QString& path) { + QFile file(path); +} diff --git a/source/javascript/module/file/FileJsModule.hpp b/source/javascript/module/file/FileJsModule.hpp new file mode 100644 index 0000000..68fae36 --- /dev/null +++ b/source/javascript/module/file/FileJsModule.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include "../_base/BaseJsModule.hpp" + + +class FileJsModule : public BaseJsModule { + Q_OBJECT + +public: + explicit FileJsModule(QJSEngine* engine, const QList& allowedDirectories); + + Q_INVOKABLE QJSValue open(const QString& path); + +private: + QList allowedDirectories; +}; diff --git a/source/javascript/module/image/ImageJsModule.cpp b/source/javascript/module/image/ImageJsModule.cpp new file mode 100644 index 0000000..4f562f3 --- /dev/null +++ b/source/javascript/module/image/ImageJsModule.cpp @@ -0,0 +1,11 @@ +#include "ImageJsModule.hpp" + +#include "ImageJsObject.hpp" + + +ImageJsModule::ImageJsModule(QJSEngine* engine) : BaseJsModule(engine) {} + +QJSValue ImageJsModule::load(const QString& path) { + // TODO(Faraphel): can the new lead to a memory leak ? + return this->engine->newQObject(new ImageJsObject(path)); +} diff --git a/source/javascript/module/image/ImageJsModule.hpp b/source/javascript/module/image/ImageJsModule.hpp new file mode 100644 index 0000000..b946710 --- /dev/null +++ b/source/javascript/module/image/ImageJsModule.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include "ImageJsObject.hpp" +#include "../_base/BaseJsModule.hpp" + + +/** + * A module to manipulate image file + */ +class ImageJsModule : public BaseJsModule { + Q_OBJECT + +public: + explicit ImageJsModule(QJSEngine* engine); + + /** + * Load an image file + * @param + */ + Q_INVOKABLE QJSValue load(const QString& path); +}; diff --git a/source/javascript/module/image/ImageJsObject.cpp b/source/javascript/module/image/ImageJsObject.cpp new file mode 100644 index 0000000..7fc55b3 --- /dev/null +++ b/source/javascript/module/image/ImageJsObject.cpp @@ -0,0 +1,20 @@ +#include "ImageJsObject.hpp" + +#include + + +ImageJsObject::ImageJsObject(const QString& path) { + this->_internal = std::make_unique(path); +} + +std::size_t ImageJsObject::getWidth() { + return this->_internal->width(); +} + +std::size_t ImageJsObject::getHeight() { + return this->_internal->height(); +} + +QSize ImageJsObject::getSize() { + return this->_internal->size(); +} diff --git a/source/javascript/module/image/ImageJsObject.hpp b/source/javascript/module/image/ImageJsObject.hpp new file mode 100644 index 0000000..71ab820 --- /dev/null +++ b/source/javascript/module/image/ImageJsObject.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + + +class ImageJsObject : public QObject { + Q_OBJECT + +public: + explicit ImageJsObject(const QString& path); + + Q_INVOKABLE std::size_t getWidth(); + Q_INVOKABLE std::size_t getHeight(); + Q_INVOKABLE QSize getSize(); + + // TODO(Faraphel): implement copy / paste, fill, shapes, mirror, resize, expand, shrink, crop, map... + +private: + std::unique_ptr _internal; +}; + diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..41f050b --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,71 @@ +#include + +#include "javascript/engine/AtlasJsEngine.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +int main(int argc, char* argv[]) { + // application informations + QApplication application(argc, argv); + QApplication::setOrganizationDomain("faraphel.fr"); + QApplication::setApplicationName("Atlas-Launcher"); + QApplication::setApplicationVersion("1.0.0"); + + // create a restricted virtual file system + const auto fileSystem = std::make_shared(); + + // mount the cache directory + fileSystem->mountDirectory( + QDir("/cache/"), + QDir("/home/faraphel/Documents/Projects/Atlas-Launcher/source/") + ); + + // mount the temporary directory + const auto temporaryDir = QTemporaryDir(); + fileSystem->mountDirectory( + QDir("/tmp/"), + QDir(temporaryDir.path()) + ); + + // create a small window + auto mainWindow = std::make_shared(); + + auto widget = std::make_shared(); + mainWindow->setCentralWidget(widget.get()); + + auto layout = std::make_shared(); + widget->setLayout(layout.get()); + + auto input = std::make_shared(); + layout->addWidget(input.get()); + + auto submit = std::make_shared(); + layout->addWidget(submit.get()); + submit->setText("Run"); + + auto output = std::make_shared(); + layout->addWidget(output.get()); + output->setEnabled(false); + + QObject::connect(submit.get(), &QPushButton::clicked, [&]() { + auto engine = std::make_shared(nullptr); + + QJSValue value = engine->evaluate(input->toPlainText()); + output->setText(value.toString()); + }); + + mainWindow->show(); + + return QApplication::exec(); +}