| #include "js_shell.h" | |
| #include <iostream> | |
| #include <fstream> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <unistd.h> | |
| #ifdef __GNUC__ | |
| #ifdef __APPLE__ | |
| #define LIBRARY_EXT ".bundle" | |
| #else | |
| #define LIBRARY_EXT ".so" | |
| #endif | |
| #include <dlfcn.h> | |
| #define LOAD_LIBRARY(name) dlopen(name, RTLD_LAZY) | |
| #define CLOSE_LIBRARY(handle) dlclose(handle) | |
| #define LIBRARY_ERROR dlerror | |
| #define LIBRARYFILE(name) std::string("lib").append(name).append(LIBRARY_EXT) | |
| #else | |
| #error "implement dll loading" | |
| #endif | |
| JSShell::~JSShell() { | |
| for(std::vector<HANDLE>::iterator it = loaded_modules.begin(); | |
| it != loaded_modules.end(); ++it) { | |
| HANDLE handle = *it; | |
| CLOSE_LIBRARY(handle); | |
| } | |
| } | |
| // TODO: this could be done more intelligent... | |
| // - can we achieve source file relative loading? | |
| // - better path resolution | |
| std::string JSShell::LoadModule(const std::string& name, HANDLE* library) { | |
| // works only for posix like OSs | |
| size_t pathIdx = name.find_last_of("/"); | |
| std::string lib_name; | |
| std::string module_name; | |
| if (pathIdx == std::string::npos) { | |
| module_name = name; | |
| lib_name = std::string(name).append(LIBRARY_EXT); | |
| } else { | |
| std::string path = name.substr(0, pathIdx+1); | |
| module_name = name.substr(pathIdx+1); | |
| lib_name = path.append(module_name).append(LIBRARY_EXT); | |
| } | |
| std::string lib_path; | |
| HANDLE handle = 0; | |
| for (int i = 0; i < module_path.size(); ++i) { | |
| lib_path = module_path[i] + "/" + lib_name; | |
| if (access( lib_path.c_str(), F_OK ) != -1) { | |
| handle = LOAD_LIBRARY(lib_path.c_str()); | |
| } | |
| } | |
| if(handle == 0) { | |
| std::cerr << "Could not find module " << lib_path << ':' | |
| << std::endl << LIBRARY_ERROR() << std::endl; | |
| // Not 'return 0' - constructing a std::string from a null pointer is ill-formed since C++23 | |
| return ""; | |
| } | |
| loaded_modules.push_back(handle); | |
| *library = handle; | |
| return module_name; | |
| } | |
| bool JSShell::RunScript(const std::string& scriptPath) { | |
| std::string source = ReadFile(scriptPath); | |
| if(!InitializeEngine()) return false; | |
| // Node.js compatibility: make `print` available as `console.log()` | |
| ExecuteScript("var console = {}; console.log = print;", "<console>"); | |
| if(!ExecuteScript(source, scriptPath)) { | |
| return false; | |
| } | |
| return DisposeEngine(); | |
| } | |
| bool JSShell::RunShell() { | |
| if(!InitializeEngine()) return false; | |
| static const int kBufferSize = 1024; | |
| while (true) { | |
| char buffer[kBufferSize]; | |
| printf("> "); | |
| char* str = fgets(buffer, kBufferSize, stdin); | |
| if (str == NULL) break; | |
| std::string source(str); | |
| ExecuteScript(source, "(shell)"); | |
| } | |
| printf("\n"); | |
| return true; | |
| } | |
| std::string JSShell::ReadFile(const std::string& fileName) | |
| { | |
| std::string script; | |
| std::ifstream file(fileName.c_str()); | |
| if (file.is_open()) { | |
| while ( file.good() ) { | |
| std::string line; | |
| getline(file, line); | |
| script.append(line); | |
| script.append("\n"); | |
| } | |
| file.close(); | |
| } else { | |
| std::cout << "Unable to open file " << fileName << '.' << std::endl; | |
| } | |
| return script; | |
| } | |
| #ifdef ENABLE_JSC | |
| extern JSShell* JSCShell_Create(); | |
| #endif | |
| #ifdef ENABLE_V8 | |
| extern JSShell* V8Shell_Create(); | |
| #endif | |
| typedef JSShell*(*ShellFactory)(); | |
| static ShellFactory js_shell_factories[2] = { | |
| #ifdef ENABLE_JSC | |
| JSCShell_Create, | |
| #else | |
| 0, | |
| #endif | |
| #ifdef ENABLE_V8 | |
| V8Shell_Create, | |
| #else | |
| 0, | |
| #endif | |
| }; | |
| JSShell *JSShell::Create(Engine engine) { | |
| if(js_shell_factories[engine] == 0) { | |
| throw "Engine not available."; | |
| } | |
| return js_shell_factories[engine](); | |
| } |