| #include <JavaScriptCore/JavaScript.h> | |
| #include "js_shell.h" | |
| #include <iostream> | |
| #include <stdio.h> | |
| #ifdef __GNUC__ | |
| #include <dlfcn.h> | |
| #define LOAD_SYMBOL(handle, name) dlsym(handle, name) | |
| #else | |
| #error "implement dll loading" | |
| #endif | |
| class JSCShell: public JSShell { | |
| typedef int (*JSCIntializer)(JSGlobalContextRef context, JSObjectRef *module); | |
| public: | |
| JSCShell() { context = 0; } | |
| virtual ~JSCShell(); | |
| protected: | |
| virtual bool InitializeEngine(); | |
| virtual bool ExecuteScript(const std::string& source, const std::string& scriptPath); | |
| virtual bool DisposeEngine(); | |
| private: | |
| JSObjectRef Import(const std::string &moduleName); | |
| static JSValueRef Print(JSContextRef context, JSObjectRef object, JSObjectRef globalobj, size_t argc, const JSValueRef args[], JSValueRef* ex); | |
| static JSValueRef Require(JSContextRef context, JSObjectRef object, JSObjectRef globalobj, size_t argc, const JSValueRef args[], JSValueRef* ex); | |
| static bool RegisterFunction(JSGlobalContextRef context, JSObjectRef object, const char* functionName, JSObjectCallAsFunctionCallback cbFunction); | |
| static void PrintError(JSContextRef, JSValueRef); | |
| private: | |
| JSGlobalContextRef context; | |
| }; | |
| JSCShell::~JSCShell() { | |
| if(context != 0) { | |
| JSGlobalContextRelease(context); | |
| context = 0; | |
| } | |
| } | |
| bool JSCShell::InitializeEngine() { | |
| if(context != 0) { | |
| JSGlobalContextRelease(context); | |
| context = 0; | |
| } | |
| // TODO: check for initialization errors | |
| context = JSGlobalContextCreate(NULL); | |
| if(context == 0) return false; | |
| JSObjectRef globalObject = JSContextGetGlobalObject(context); | |
| // store this for later use | |
| JSClassDefinition __shell_classdef__ = JSClassDefinition(); | |
| JSClassRef __shell_class__ = JSClassCreate(&__shell_classdef__); | |
| JSObjectRef __shell__ = JSObjectMake(context, __shell_class__, 0); | |
| bool success = JSObjectSetPrivate(__shell__, (void*) (long) this); | |
| if (!success) { | |
| std::cerr << "Could not register the shell in the Javascript context" << std::endl; | |
| return false; | |
| } | |
| JSStringRef shellKey = JSStringCreateWithUTF8CString("__shell__"); | |
| JSObjectSetProperty(context, globalObject, shellKey, __shell__, kJSPropertyAttributeReadOnly, NULL); | |
| JSStringRelease(shellKey); | |
| JSCShell::RegisterFunction(context, globalObject, "print", JSCShell::Print); | |
| JSCShell::RegisterFunction(context, globalObject, "require", JSCShell::Require); | |
| return true; | |
| } | |
| bool JSCShell::ExecuteScript(const std::string& source, const std::string& scriptPath) { | |
| JSStringRef jsScript; | |
| JSStringRef sourceURL; | |
| JSValueRef ex; | |
| jsScript = JSStringCreateWithUTF8CString(source.c_str()); | |
| sourceURL = JSStringCreateWithUTF8CString(scriptPath.c_str()); | |
| JSValueRef jsResult = JSEvaluateScript(context, jsScript, 0, sourceURL, 0, &ex); | |
| JSStringRelease(jsScript); | |
| if (jsResult == NULL && ex != NULL) { | |
| JSCShell::PrintError(context, ex); | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool JSCShell::DisposeEngine() { | |
| JSGlobalContextRelease(context); | |
| context = 0; | |
| return true; | |
| } | |
| JSValueRef JSCShell::Print(JSContextRef context, JSObjectRef object, | |
| JSObjectRef globalobj, size_t argc, | |
| const JSValueRef args[], JSValueRef* ex) { | |
| if (argc > 0) | |
| { | |
| JSStringRef string = JSValueToStringCopy(context, args[0], NULL); | |
| size_t numChars = JSStringGetMaximumUTF8CStringSize(string); | |
| char *stringUTF8 = new char[numChars]; | |
| JSStringGetUTF8CString(string, stringUTF8, numChars); | |
| printf("%s\n", stringUTF8); | |
| delete[] stringUTF8; | |
| } | |
| return JSValueMakeUndefined(context); | |
| } | |
| // Attention: this feature should not create too high expectations. | |
| // It is only capable of loading things relative to the execution directory | |
| // and not relative to the parent script. | |
| JSValueRef JSCShell::Require(JSContextRef context, JSObjectRef object, | |
| JSObjectRef globalObj, size_t argc, | |
| const JSValueRef args[], JSValueRef* ex) { | |
| JSObjectRef module; | |
| JSStringRef shellKey = JSStringCreateWithUTF8CString("__shell__"); | |
| JSValueRef shellAsVal = JSObjectGetProperty(context, globalObj, shellKey, NULL); | |
| JSStringRelease(shellKey); | |
| JSObjectRef shell = JSValueToObject(context, shellAsVal, 0); | |
| JSCShell *_this = (JSCShell*) (long) JSObjectGetPrivate(shell); | |
| if (argc > 0) | |
| { | |
| JSStringRef string = JSValueToStringCopy(context, args[0], NULL); | |
| size_t numChars = JSStringGetMaximumUTF8CStringSize(string); | |
| char *stringUTF8 = new char[numChars]; | |
| JSStringGetUTF8CString(string, stringUTF8, numChars); | |
| std::string modulePath(stringUTF8); | |
| module = _this->Import(modulePath); | |
| delete[] stringUTF8; | |
| } | |
| if (module) { | |
| return module; | |
| } else { | |
| printf("Ooops.\n"); | |
| return JSValueMakeUndefined(context); | |
| } | |
| } | |
| JSObjectRef JSCShell::Import(const std::string& module_path) { | |
| HANDLE library; | |
| std::string module_name = LoadModule(module_path, &library); | |
| if (library == 0) { | |
| printf("Could not load module."); | |
| return 0; | |
| } | |
| std::string symname = std::string(module_name).append("_initialize"); | |
| JSCIntializer init_function = reinterpret_cast<JSCIntializer>((long) LOAD_SYMBOL(library, symname.c_str())); | |
| if(init_function == 0) { | |
| printf("Could not find module's initializer function."); | |
| return 0; | |
| } | |
| JSObjectRef module; | |
| init_function(context, &module); | |
| return module; | |
| } | |
| bool JSCShell::RegisterFunction(JSGlobalContextRef context, JSObjectRef object, | |
| const char* functionName, JSObjectCallAsFunctionCallback callback) { | |
| JSStringRef js_functionName = JSStringCreateWithUTF8CString(functionName); | |
| JSObjectSetProperty(context, object, js_functionName, | |
| JSObjectMakeFunctionWithCallback(context, js_functionName, callback), | |
| kJSPropertyAttributeNone, NULL); | |
| JSStringRelease(js_functionName); | |
| return true; | |
| } | |
| void JSCShell::PrintError(JSContextRef ctx, JSValueRef err) { | |
| char *buffer; | |
| size_t length; | |
| JSStringRef string = JSValueToStringCopy(ctx, err, 0); | |
| length = JSStringGetLength(string); | |
| buffer = new char[length+1]; | |
| JSStringGetUTF8CString(string, buffer, length+1); | |
| std::string errMsg(buffer); | |
| JSStringRelease(string); | |
| delete[] buffer; | |
| JSObjectRef errObj = JSValueToObject(ctx, err, 0); | |
| if(errObj == 0) { | |
| std::cerr << errMsg << std::endl; | |
| return; | |
| } | |
| JSStringRef sourceURLKey = JSStringCreateWithUTF8CString("sourceURL"); | |
| JSStringRef sourceURLStr = JSValueToStringCopy(ctx, JSObjectGetProperty(ctx, errObj, sourceURLKey, 0), 0); | |
| length = JSStringGetLength(sourceURLStr); | |
| buffer = new char[length+1]; | |
| JSStringGetUTF8CString(sourceURLStr, buffer, length+1); | |
| std::string sourceURL(buffer); | |
| delete[] buffer; | |
| JSStringRelease(sourceURLStr); | |
| JSStringRelease(sourceURLKey); | |
| JSStringRef lineKey = JSStringCreateWithUTF8CString("line"); | |
| JSValueRef jsLine = JSObjectGetProperty(ctx, errObj, lineKey, 0); | |
| int line = (int) JSValueToNumber(ctx, jsLine, 0); | |
| JSStringRelease(lineKey); | |
| std::cerr << sourceURL << ':' << line << ':' << errMsg << std::endl; | |
| } | |
| JSShell* JSCShell_Create() { | |
| return new JSCShell(); | |
| } |