diff options
author | Paul Gardiner <paul.gardiner@artifex.com> | 2013-11-28 12:45:18 +0000 |
---|---|---|
committer | Paul Gardiner <paul.gardiner@artifex.com> | 2013-12-16 17:33:14 +0000 |
commit | 193060841a2669adcf905907581d2c701f0d02f4 (patch) | |
tree | 6971be84043995d2cc9e97553b4ce7fd38d8fc7d | |
parent | 2380744fddfe7d47d07a175fb3a55d0b22af1eb2 (diff) | |
download | mupdf-193060841a2669adcf905907581d2c701f0d02f4.tar.xz |
JavaScriptCore-based implementation of the MuPDF JS engine API
-rw-r--r-- | source/pdf/js/pdf-jsimp-jscore.c | 519 | ||||
-rw-r--r-- | source/pdf/js/pdf-jsimp-v8.cpp | 4 |
2 files changed, 520 insertions, 3 deletions
diff --git a/source/pdf/js/pdf-jsimp-jscore.c b/source/pdf/js/pdf-jsimp-jscore.c new file mode 100644 index 00000000..55808b4c --- /dev/null +++ b/source/pdf/js/pdf-jsimp-jscore.c @@ -0,0 +1,519 @@ +/* This file contains wrapper functions for pdf_jsimp functions implemented + * in Javascriptcore */ + +#include <JavaScriptCore/JavaScriptCore.h> +#include "mupdf/pdf.h" + +#define STRING_BUF_SIZE (256) +#define FUNCTION_PREAMBLE_LEN (9) + +/* + We need only a single JSClassRef because we store property and method information + in the private data of each object. The JSClassRef is set up to know how to access + that data. +*/ +struct pdf_jsimp_s +{ + fz_context *ctx; + void *nat_ctx; + JSGlobalContextRef jscore_ctx; + JSClassRef class_ref; +}; + +enum +{ + PROP_FN, + PROP_VAL +}; + +typedef struct prop_fn_s +{ + pdf_jsimp_method *meth; +} prop_fn; + +typedef struct prop_val_s +{ + pdf_jsimp_getter *get; + pdf_jsimp_setter *set; +} prop_val; + +typedef struct prop_s +{ + char *name; + int type; + union + { + prop_fn fn; + prop_val val; + } u; +} prop; + +typedef struct prop_list_s prop_list; + +struct prop_list_s +{ + prop prop; + prop_list *next; +}; + +struct pdf_jsimp_type_s +{ + pdf_jsimp *imp; + pdf_jsimp_dtr *dtr; + prop_list *props; +}; + +/* + When we create a JavaScriptCore object, we store in its private data the MuPDF + native object pointer and a pointer to the type. The type has a list of the + properties and methods +*/ +typedef struct priv_data_s +{ + pdf_jsimp_type *type; + void *natobj; +} priv_data; + +struct pdf_jsimp_obj_s +{ + JSValueRef ref; + char *str; +}; + +static prop *find_prop(prop_list *list, char *name) +{ + while (list) + { + if (strcmp(name, list->prop.name) == 0) + return &list->prop; + + list = list->next; + } + + return NULL; +} + +static pdf_jsimp_obj *wrap_val(pdf_jsimp *imp, JSValueRef ref) +{ + pdf_jsimp_obj *obj = fz_malloc_struct(imp->ctx, pdf_jsimp_obj); + obj->ref = ref; + JSValueProtect(imp->jscore_ctx, ref); + + return obj; +} + +static JSValueRef callMethod(JSContextRef jscore_ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) +{ + pdf_jsimp *imp; + fz_context *ctx; + pdf_jsimp_obj *res = NULL; + JSValueRef resref = NULL; + int i; + pdf_jsimp_obj **args = NULL; + pdf_jsimp_method *meth = JSObjectGetPrivate(function); + priv_data *pdata = JSObjectGetPrivate(thisObject); + if (meth == NULL) + { + /* + The attempt to store the method pointer as private data failed, so we + turn the function into a string, which will have the form "function name() xxx", + and then lookup the name. + */ + char name[STRING_BUF_SIZE]; + char *np; + char *bp; + JSStringRef jname = JSValueToStringCopy(jscore_ctx, function, NULL); + prop *p; + JSStringGetUTF8CString(jname, name, STRING_BUF_SIZE); + if (strlen(name) >= FUNCTION_PREAMBLE_LEN) + { + np = name + FUNCTION_PREAMBLE_LEN; /* strlen("function "); */ + bp = strchr(np, '('); + if (bp) + *bp = 0; + p = find_prop(pdata->type->props, np); + if (p && p->type == PROP_FN) + { + meth = p->u.fn.meth; + } + } + JSStringRelease(jname); + } + if (meth == NULL || pdata == NULL) + return JSValueMakeUndefined(jscore_ctx); + + imp = pdata->type->imp; + ctx = imp->ctx; + + fz_var(args); + fz_var(res); + fz_try(ctx) + { + args = fz_malloc_array(ctx, argumentCount, sizeof(pdf_jsimp_obj)); + for (i = 0; i < argumentCount; i++) + args[i] = wrap_val(imp, arguments[i]); + + res = meth(imp->nat_ctx, pdata->natobj, argumentCount, args); + if (res) + resref = res->ref; + } + fz_always(ctx) + { + if (args) + { + for (i = 0; i < argumentCount; i++) + pdf_jsimp_drop_obj(imp, args[i]); + fz_free(ctx, args); + } + pdf_jsimp_drop_obj(imp, res); + } + fz_catch(ctx) + { + return JSValueMakeUndefined(jscore_ctx); + } + + return resref; +} + +static JSValueRef getProperty(JSContextRef jscore_ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) +{ + pdf_jsimp *imp; + char buf[STRING_BUF_SIZE]; + prop *p; + JSValueRef res = NULL; + + priv_data *pdata = JSObjectGetPrivate(object); + if (pdata == NULL) + return NULL; + + JSStringGetUTF8CString(propertyName, buf, STRING_BUF_SIZE); + p = find_prop(pdata->type->props, buf); + if (p == NULL) + return NULL; + + imp = pdata->type->imp; + + switch(p->type) + { + case PROP_FN: + { + /* + For some reason passing the method pointer as private data doesn't work: the data comes back + NULL when interrogated in callMethod above. So we also specify the method name when + creating the function so that we can look it up again in callMethod. Not ideal, but + will do until we can find a better solution. + */ + JSObjectRef ores = JSObjectMakeFunctionWithCallback(jscore_ctx, propertyName, callMethod); + JSObjectSetPrivate(ores, p->u.fn.meth); + res = ores; + } + break; + + case PROP_VAL: + { + pdf_jsimp_obj *pres = p->u.val.get(imp->nat_ctx, pdata->natobj); + res = pres->ref; + pdf_jsimp_drop_obj(imp, pres); + } + break; + } + + return res; +} + +static bool setProperty(JSContextRef jscore_ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef *exception) +{ + pdf_jsimp *imp; + char buf[STRING_BUF_SIZE]; + prop *p; + + priv_data *pdata = JSObjectGetPrivate(object); + if (pdata == NULL) + return false; + + JSStringGetUTF8CString(propertyName, buf, STRING_BUF_SIZE); + p = find_prop(pdata->type->props, buf); + if (p == NULL) + return false; + + imp = pdata->type->imp; + + switch(p->type) + { + case PROP_FN: + break; + + case PROP_VAL: + { + pdf_jsimp_obj *pval = wrap_val(imp, value); + p->u.val.set(imp->nat_ctx, pdata->natobj, pval); + pdf_jsimp_drop_obj(imp, pval); + } + break; + } + + return true; +} + +pdf_jsimp *pdf_new_jsimp(fz_context *ctx, void *jsctx) +{ + pdf_jsimp *imp = fz_malloc_struct(ctx, pdf_jsimp); + + fz_try(ctx) + { + JSClassDefinition classDef = kJSClassDefinitionEmpty; + + classDef.getProperty = getProperty; + classDef.setProperty = setProperty; + + imp->nat_ctx = jsctx; + imp->class_ref = JSClassCreate(&classDef); + imp->jscore_ctx = JSGlobalContextCreate(imp->class_ref); + if (imp->jscore_ctx == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "JSGlobalContextCreate failed"); + } + fz_catch(ctx) + { + pdf_drop_jsimp(imp); + fz_rethrow(ctx); + } + + imp->ctx = ctx; + + return imp; +} + +void pdf_drop_jsimp(pdf_jsimp *imp) +{ + if (imp) + { + JSGlobalContextRelease(imp->jscore_ctx); + JSClassRelease(imp->class_ref); + fz_free(imp->ctx, imp); + } +} + +pdf_jsimp_type *pdf_jsimp_new_type(pdf_jsimp *imp, pdf_jsimp_dtr *dtr) +{ + pdf_jsimp_type *type = fz_malloc_struct(imp->ctx, pdf_jsimp_type); + type->imp = imp; + type->dtr = dtr; + + return type; +} + +void pdf_jsimp_drop_type(pdf_jsimp *imp, pdf_jsimp_type *type) +{ + if (imp && type) + { + fz_context *ctx = imp->ctx; + prop_list *node; + + while (type->props) + { + node = type->props; + type->props = node->next; + fz_free(ctx, node->prop.name); + fz_free(ctx, node); + } + + fz_free(ctx, type); + } +} + +void pdf_jsimp_addmethod(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_method *meth) +{ + fz_context *ctx = imp->ctx; + prop_list *node = fz_malloc_struct(ctx, prop_list); + + fz_try(ctx) + { + node->prop.name = fz_strdup(imp->ctx, name); + node->prop.type = PROP_FN; + node->prop.u.fn.meth = meth; + + node->next = type->props; + type->props = node; + } + fz_catch(ctx) + { + fz_free(ctx, node); + fz_rethrow(ctx); + } +} + +void pdf_jsimp_addproperty(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_getter *get, pdf_jsimp_setter *set) +{ + fz_context *ctx = imp->ctx; + prop_list *node = fz_malloc_struct(ctx, prop_list); + + fz_try(ctx) + { + node->prop.name = fz_strdup(imp->ctx, name); + node->prop.type = PROP_VAL; + node->prop.u.val.get = get; + node->prop.u.val.set = set; + + node->next = type->props; + type->props = node; + } + fz_catch(ctx) + { + fz_free(ctx, node); + fz_rethrow(ctx); + } +} + +void pdf_jsimp_set_global_type(pdf_jsimp *imp, pdf_jsimp_type *type) +{ + fz_context *ctx = imp->ctx; + priv_data *pdata; + JSObjectRef gobj = JSContextGetGlobalObject(imp->jscore_ctx); + if (gobj == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "JSContextGetGlobalObject failed"); + + pdata = fz_malloc_struct(ctx, priv_data); + pdata->type = type; + pdata->natobj = NULL; + JSObjectSetPrivate(gobj, pdata); +} + +pdf_jsimp_obj *pdf_jsimp_new_obj(pdf_jsimp *imp, pdf_jsimp_type *type, void *natobj) +{ + fz_context *ctx = imp->ctx; + pdf_jsimp_obj *obj = fz_malloc_struct(ctx, pdf_jsimp_obj); + priv_data *pdata = NULL; + + fz_var(pdata); + fz_try(ctx) + { + pdata = fz_malloc_struct(ctx, priv_data); + pdata->type = type; + pdata->natobj = natobj; + obj->ref = JSObjectMake(imp->jscore_ctx, imp->class_ref, pdata); + if (obj->ref == NULL) + fz_throw(ctx, FZ_ERROR_GENERIC, "JSObjectMake failed"); + + JSValueProtect(imp->jscore_ctx, obj->ref); + } + fz_catch(ctx) + { + fz_free(ctx, pdata); + fz_free(ctx, obj); + fz_rethrow(ctx); + } + + return obj; +} + +void pdf_jsimp_drop_obj(pdf_jsimp *imp, pdf_jsimp_obj *obj) +{ + if (imp && obj) + { + JSValueUnprotect(imp->jscore_ctx, obj->ref); + fz_free(imp->ctx, obj->str); + fz_free(imp->ctx, obj); + } +} + +int pdf_jsimp_to_type(pdf_jsimp *imp, pdf_jsimp_obj *obj) +{ + switch (JSValueGetType(imp->jscore_ctx, obj->ref)) + { + case kJSTypeNull: return JS_TYPE_NULL; + case kJSTypeBoolean: return JS_TYPE_BOOLEAN; + case kJSTypeNumber: return JS_TYPE_NUMBER; + case kJSTypeString: return JS_TYPE_STRING; + case kJSTypeObject: return JS_TYPE_ARRAY; + default: return JS_TYPE_UNKNOWN; + } +} + +pdf_jsimp_obj *pdf_jsimp_from_string(pdf_jsimp *imp, char *str) +{ + JSStringRef sref = JSStringCreateWithUTF8CString(str); + JSValueRef vref = JSValueMakeString(imp->jscore_ctx, sref); + JSStringRelease(sref); + + return wrap_val(imp, vref); +} + +char *pdf_jsimp_to_string(pdf_jsimp *imp, pdf_jsimp_obj *obj) +{ + fz_context *ctx = imp->ctx; + JSStringRef jstr = JSValueToStringCopy(imp->jscore_ctx, obj->ref, NULL); + int len; + + if (jstr == NULL) + return ""; + + fz_try(ctx) + { + len = JSStringGetMaximumUTF8CStringSize(jstr); + fz_free(ctx, obj->str); + obj->str = NULL; + obj->str = fz_malloc(ctx, len+1); + JSStringGetUTF8CString(jstr, obj->str, len+1); + } + fz_always(ctx) + { + JSStringRelease(jstr); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } + + return obj->str; +} + +pdf_jsimp_obj *pdf_jsimp_from_number(pdf_jsimp *imp, double num) +{ + return wrap_val(imp, JSValueMakeNumber(imp->jscore_ctx, num)); +} + +double pdf_jsimp_to_number(pdf_jsimp *imp, pdf_jsimp_obj *obj) +{ + return JSValueToNumber(imp->jscore_ctx, obj->ref, NULL); +} + +int pdf_jsimp_array_len(pdf_jsimp *imp, pdf_jsimp_obj *obj) +{ + pdf_jsimp_obj *lobj = pdf_jsimp_property(imp, obj, "length"); + int num = (int)pdf_jsimp_to_number(imp, lobj); + + pdf_jsimp_drop_obj(imp, lobj); + + return num; +} + +pdf_jsimp_obj *pdf_jsimp_array_item(pdf_jsimp *imp, pdf_jsimp_obj *obj, int i) +{ + return wrap_val(imp, JSObjectGetPropertyAtIndex(imp->jscore_ctx, JSValueToObject(imp->jscore_ctx, obj->ref, NULL), i, NULL)); +} + +pdf_jsimp_obj *pdf_jsimp_property(pdf_jsimp *imp, pdf_jsimp_obj *obj, char *prop) +{ + JSStringRef jprop = JSStringCreateWithUTF8CString(prop); + JSValueRef jval = JSObjectGetProperty(imp->jscore_ctx, JSValueToObject(imp->jscore_ctx, obj->ref, NULL), jprop, NULL); + + JSStringRelease(jprop); + + return wrap_val(imp, jval); +} + +void pdf_jsimp_execute(pdf_jsimp *imp, char *code) +{ + JSStringRef jcode = JSStringCreateWithUTF8CString(code); + JSEvaluateScript(imp->jscore_ctx, jcode, NULL, NULL, 0, NULL); + JSStringRelease(jcode); +} + +void pdf_jsimp_execute_count(pdf_jsimp *imp, char *code, int count) +{ + char *terminated = fz_malloc(imp->ctx, count+1); + memcpy(terminated, code, count); + terminated[count] = 0; + pdf_jsimp_execute(imp, terminated); + fz_free(imp->ctx, terminated); +} diff --git a/source/pdf/js/pdf-jsimp-v8.cpp b/source/pdf/js/pdf-jsimp-v8.cpp index f3894b6b..0a6d5928 100644 --- a/source/pdf/js/pdf-jsimp-v8.cpp +++ b/source/pdf/js/pdf-jsimp-v8.cpp @@ -1,7 +1,5 @@ /* - * This is a dummy JavaScript engine. It cheats by recognising the specific - * strings in calc.pdf, and hence will work only for that file. It is for - * testing only. + This file contains the v8 implementation of the pdf_jsimp API */ extern "C" { |