#include #include "js_shell.h" #include #include #ifdef __GNUC__ #include #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((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(); }