summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorPaul Gardiner <paul.gardiner@artifex.com>2013-11-28 12:45:18 +0000
committerPaul Gardiner <paul.gardiner@artifex.com>2013-12-16 17:33:14 +0000
commit193060841a2669adcf905907581d2c701f0d02f4 (patch)
tree6971be84043995d2cc9e97553b4ce7fd38d8fc7d /source
parent2380744fddfe7d47d07a175fb3a55d0b22af1eb2 (diff)
downloadmupdf-193060841a2669adcf905907581d2c701f0d02f4.tar.xz
JavaScriptCore-based implementation of the MuPDF JS engine API
Diffstat (limited to 'source')
-rw-r--r--source/pdf/js/pdf-jsimp-jscore.c519
-rw-r--r--source/pdf/js/pdf-jsimp-v8.cpp4
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" {