Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
7643 views
1
/* This file contains wrapper functions for pdf_jsimp functions implemented
2
* in Javascriptcore */
3
4
#include <JavaScriptCore/JavaScriptCore.h>
5
#include "mupdf/pdf.h"
6
7
#define STRING_BUF_SIZE (256)
8
#define FUNCTION_PREAMBLE_LEN (9)
9
10
/*
11
We need only a single JSClassRef because we store property and method information
12
in the private data of each object. The JSClassRef is set up to know how to access
13
that data.
14
*/
15
struct pdf_jsimp_s
16
{
17
fz_context *ctx;
18
void *nat_ctx;
19
JSGlobalContextRef jscore_ctx;
20
JSClassRef class_ref;
21
};
22
23
enum
24
{
25
PROP_FN,
26
PROP_VAL
27
};
28
29
typedef struct prop_fn_s
30
{
31
pdf_jsimp_method *meth;
32
} prop_fn;
33
34
typedef struct prop_val_s
35
{
36
pdf_jsimp_getter *get;
37
pdf_jsimp_setter *set;
38
} prop_val;
39
40
typedef struct prop_s
41
{
42
char *name;
43
int type;
44
union
45
{
46
prop_fn fn;
47
prop_val val;
48
} u;
49
} prop;
50
51
typedef struct prop_list_s prop_list;
52
53
struct prop_list_s
54
{
55
prop prop;
56
prop_list *next;
57
};
58
59
struct pdf_jsimp_type_s
60
{
61
pdf_jsimp *imp;
62
pdf_jsimp_dtr *dtr;
63
prop_list *props;
64
};
65
66
/*
67
When we create a JavaScriptCore object, we store in its private data the MuPDF
68
native object pointer and a pointer to the type. The type has a list of the
69
properties and methods
70
*/
71
typedef struct priv_data_s
72
{
73
pdf_jsimp_type *type;
74
void *natobj;
75
} priv_data;
76
77
struct pdf_jsimp_obj_s
78
{
79
JSValueRef ref;
80
char *str;
81
};
82
83
static prop *find_prop(prop_list *list, char *name)
84
{
85
while (list)
86
{
87
if (strcmp(name, list->prop.name) == 0)
88
return &list->prop;
89
90
list = list->next;
91
}
92
93
return NULL;
94
}
95
96
static pdf_jsimp_obj *wrap_val(pdf_jsimp *imp, JSValueRef ref)
97
{
98
pdf_jsimp_obj *obj = fz_malloc_struct(imp->ctx, pdf_jsimp_obj);
99
obj->ref = ref;
100
JSValueProtect(imp->jscore_ctx, ref);
101
102
return obj;
103
}
104
105
static JSValueRef callMethod(JSContextRef jscore_ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception)
106
{
107
pdf_jsimp *imp;
108
fz_context *ctx;
109
pdf_jsimp_obj *res = NULL;
110
JSValueRef resref = NULL;
111
int i;
112
pdf_jsimp_obj **args = NULL;
113
pdf_jsimp_method *meth = JSObjectGetPrivate(function);
114
priv_data *pdata = JSObjectGetPrivate(thisObject);
115
if (meth == NULL)
116
{
117
/*
118
The attempt to store the method pointer as private data failed, so we
119
turn the function into a string, which will have the form "function name() xxx",
120
and then lookup the name.
121
*/
122
char name[STRING_BUF_SIZE];
123
char *np;
124
char *bp;
125
JSStringRef jname = JSValueToStringCopy(jscore_ctx, function, NULL);
126
prop *p;
127
JSStringGetUTF8CString(jname, name, STRING_BUF_SIZE);
128
if (strlen(name) >= FUNCTION_PREAMBLE_LEN)
129
{
130
np = name + FUNCTION_PREAMBLE_LEN; /* strlen("function "); */
131
bp = strchr(np, '(');
132
if (bp)
133
*bp = 0;
134
p = find_prop(pdata->type->props, np);
135
if (p && p->type == PROP_FN)
136
{
137
meth = p->u.fn.meth;
138
}
139
}
140
JSStringRelease(jname);
141
}
142
if (meth == NULL || pdata == NULL)
143
return JSValueMakeUndefined(jscore_ctx);
144
145
imp = pdata->type->imp;
146
ctx = imp->ctx;
147
148
fz_var(args);
149
fz_var(res);
150
fz_try(ctx)
151
{
152
args = fz_malloc_array(ctx, argumentCount, sizeof(pdf_jsimp_obj));
153
for (i = 0; i < argumentCount; i++)
154
args[i] = wrap_val(imp, arguments[i]);
155
156
res = meth(imp->nat_ctx, pdata->natobj, argumentCount, args);
157
if (res)
158
resref = res->ref;
159
}
160
fz_always(ctx)
161
{
162
if (args)
163
{
164
for (i = 0; i < argumentCount; i++)
165
pdf_jsimp_drop_obj(imp, args[i]);
166
fz_free(ctx, args);
167
}
168
pdf_jsimp_drop_obj(imp, res);
169
}
170
fz_catch(ctx)
171
{
172
return JSValueMakeUndefined(jscore_ctx);
173
}
174
175
return resref;
176
}
177
178
static JSValueRef getProperty(JSContextRef jscore_ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception)
179
{
180
pdf_jsimp *imp;
181
char buf[STRING_BUF_SIZE];
182
prop *p;
183
JSValueRef res = NULL;
184
185
priv_data *pdata = JSObjectGetPrivate(object);
186
if (pdata == NULL)
187
return NULL;
188
189
JSStringGetUTF8CString(propertyName, buf, STRING_BUF_SIZE);
190
p = find_prop(pdata->type->props, buf);
191
if (p == NULL)
192
return NULL;
193
194
imp = pdata->type->imp;
195
196
switch(p->type)
197
{
198
case PROP_FN:
199
{
200
/*
201
For some reason passing the method pointer as private data doesn't work: the data comes back
202
NULL when interrogated in callMethod above. So we also specify the method name when
203
creating the function so that we can look it up again in callMethod. Not ideal, but
204
will do until we can find a better solution.
205
*/
206
JSObjectRef ores = JSObjectMakeFunctionWithCallback(jscore_ctx, propertyName, callMethod);
207
JSObjectSetPrivate(ores, p->u.fn.meth);
208
res = ores;
209
}
210
break;
211
212
case PROP_VAL:
213
{
214
pdf_jsimp_obj *pres = p->u.val.get(imp->nat_ctx, pdata->natobj);
215
res = pres->ref;
216
pdf_jsimp_drop_obj(imp, pres);
217
}
218
break;
219
}
220
221
return res;
222
}
223
224
static bool setProperty(JSContextRef jscore_ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef *exception)
225
{
226
pdf_jsimp *imp;
227
char buf[STRING_BUF_SIZE];
228
prop *p;
229
230
priv_data *pdata = JSObjectGetPrivate(object);
231
if (pdata == NULL)
232
return false;
233
234
JSStringGetUTF8CString(propertyName, buf, STRING_BUF_SIZE);
235
p = find_prop(pdata->type->props, buf);
236
if (p == NULL)
237
return false;
238
239
imp = pdata->type->imp;
240
241
switch(p->type)
242
{
243
case PROP_FN:
244
break;
245
246
case PROP_VAL:
247
{
248
pdf_jsimp_obj *pval = wrap_val(imp, value);
249
p->u.val.set(imp->nat_ctx, pdata->natobj, pval);
250
pdf_jsimp_drop_obj(imp, pval);
251
}
252
break;
253
}
254
255
return true;
256
}
257
258
pdf_jsimp *pdf_new_jsimp(fz_context *ctx, void *jsctx)
259
{
260
pdf_jsimp *imp = fz_malloc_struct(ctx, pdf_jsimp);
261
262
fz_try(ctx)
263
{
264
JSClassDefinition classDef = kJSClassDefinitionEmpty;
265
266
classDef.getProperty = getProperty;
267
classDef.setProperty = setProperty;
268
269
imp->nat_ctx = jsctx;
270
imp->class_ref = JSClassCreate(&classDef);
271
imp->jscore_ctx = JSGlobalContextCreate(imp->class_ref);
272
if (imp->jscore_ctx == NULL)
273
fz_throw(ctx, FZ_ERROR_GENERIC, "JSGlobalContextCreate failed");
274
}
275
fz_catch(ctx)
276
{
277
pdf_drop_jsimp(imp);
278
fz_rethrow(ctx);
279
}
280
281
imp->ctx = ctx;
282
283
return imp;
284
}
285
286
void pdf_drop_jsimp(pdf_jsimp *imp)
287
{
288
if (imp)
289
{
290
JSGlobalContextRelease(imp->jscore_ctx);
291
JSClassRelease(imp->class_ref);
292
fz_free(imp->ctx, imp);
293
}
294
}
295
296
pdf_jsimp_type *pdf_jsimp_new_type(pdf_jsimp *imp, pdf_jsimp_dtr *dtr, char *name)
297
{
298
pdf_jsimp_type *type = fz_malloc_struct(imp->ctx, pdf_jsimp_type);
299
type->imp = imp;
300
type->dtr = dtr;
301
return type;
302
}
303
304
void pdf_jsimp_drop_type(pdf_jsimp *imp, pdf_jsimp_type *type)
305
{
306
if (imp && type)
307
{
308
fz_context *ctx = imp->ctx;
309
prop_list *node;
310
311
while (type->props)
312
{
313
node = type->props;
314
type->props = node->next;
315
fz_free(ctx, node->prop.name);
316
fz_free(ctx, node);
317
}
318
319
fz_free(ctx, type);
320
}
321
}
322
323
void pdf_jsimp_addmethod(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_method *meth)
324
{
325
fz_context *ctx = imp->ctx;
326
prop_list *node = fz_malloc_struct(ctx, prop_list);
327
328
fz_try(ctx)
329
{
330
node->prop.name = fz_strdup(imp->ctx, name);
331
node->prop.type = PROP_FN;
332
node->prop.u.fn.meth = meth;
333
334
node->next = type->props;
335
type->props = node;
336
}
337
fz_catch(ctx)
338
{
339
fz_free(ctx, node);
340
fz_rethrow(ctx);
341
}
342
}
343
344
void pdf_jsimp_addproperty(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_getter *get, pdf_jsimp_setter *set)
345
{
346
fz_context *ctx = imp->ctx;
347
prop_list *node = fz_malloc_struct(ctx, prop_list);
348
349
fz_try(ctx)
350
{
351
node->prop.name = fz_strdup(imp->ctx, name);
352
node->prop.type = PROP_VAL;
353
node->prop.u.val.get = get;
354
node->prop.u.val.set = set;
355
356
node->next = type->props;
357
type->props = node;
358
}
359
fz_catch(ctx)
360
{
361
fz_free(ctx, node);
362
fz_rethrow(ctx);
363
}
364
}
365
366
void pdf_jsimp_set_global_type(pdf_jsimp *imp, pdf_jsimp_type *type)
367
{
368
fz_context *ctx = imp->ctx;
369
priv_data *pdata;
370
JSObjectRef gobj = JSContextGetGlobalObject(imp->jscore_ctx);
371
if (gobj == NULL)
372
fz_throw(ctx, FZ_ERROR_GENERIC, "JSContextGetGlobalObject failed");
373
374
pdata = fz_malloc_struct(ctx, priv_data);
375
pdata->type = type;
376
pdata->natobj = NULL;
377
JSObjectSetPrivate(gobj, pdata);
378
}
379
380
pdf_jsimp_obj *pdf_jsimp_new_obj(pdf_jsimp *imp, pdf_jsimp_type *type, void *natobj)
381
{
382
fz_context *ctx = imp->ctx;
383
pdf_jsimp_obj *obj = fz_malloc_struct(ctx, pdf_jsimp_obj);
384
priv_data *pdata = NULL;
385
386
fz_var(pdata);
387
fz_try(ctx)
388
{
389
pdata = fz_malloc_struct(ctx, priv_data);
390
pdata->type = type;
391
pdata->natobj = natobj;
392
obj->ref = JSObjectMake(imp->jscore_ctx, imp->class_ref, pdata);
393
if (obj->ref == NULL)
394
fz_throw(ctx, FZ_ERROR_GENERIC, "JSObjectMake failed");
395
396
JSValueProtect(imp->jscore_ctx, obj->ref);
397
}
398
fz_catch(ctx)
399
{
400
fz_free(ctx, pdata);
401
fz_free(ctx, obj);
402
fz_rethrow(ctx);
403
}
404
405
return obj;
406
}
407
408
void pdf_jsimp_drop_obj(pdf_jsimp *imp, pdf_jsimp_obj *obj)
409
{
410
if (imp && obj)
411
{
412
JSValueUnprotect(imp->jscore_ctx, obj->ref);
413
fz_free(imp->ctx, obj->str);
414
fz_free(imp->ctx, obj);
415
}
416
}
417
418
int pdf_jsimp_to_type(pdf_jsimp *imp, pdf_jsimp_obj *obj)
419
{
420
switch (JSValueGetType(imp->jscore_ctx, obj->ref))
421
{
422
case kJSTypeNull: return JS_TYPE_NULL;
423
case kJSTypeBoolean: return JS_TYPE_BOOLEAN;
424
case kJSTypeNumber: return JS_TYPE_NUMBER;
425
case kJSTypeString: return JS_TYPE_STRING;
426
case kJSTypeObject: return JS_TYPE_ARRAY;
427
default: return JS_TYPE_UNKNOWN;
428
}
429
}
430
431
pdf_jsimp_obj *pdf_jsimp_from_string(pdf_jsimp *imp, char *str)
432
{
433
JSStringRef sref = JSStringCreateWithUTF8CString(str);
434
JSValueRef vref = JSValueMakeString(imp->jscore_ctx, sref);
435
JSStringRelease(sref);
436
437
return wrap_val(imp, vref);
438
}
439
440
char *pdf_jsimp_to_string(pdf_jsimp *imp, pdf_jsimp_obj *obj)
441
{
442
fz_context *ctx = imp->ctx;
443
JSStringRef jstr = JSValueToStringCopy(imp->jscore_ctx, obj->ref, NULL);
444
int len;
445
446
if (jstr == NULL)
447
return "";
448
449
fz_try(ctx)
450
{
451
len = JSStringGetMaximumUTF8CStringSize(jstr);
452
fz_free(ctx, obj->str);
453
obj->str = NULL;
454
obj->str = fz_malloc(ctx, len+1);
455
JSStringGetUTF8CString(jstr, obj->str, len+1);
456
}
457
fz_always(ctx)
458
{
459
JSStringRelease(jstr);
460
}
461
fz_catch(ctx)
462
{
463
fz_rethrow(ctx);
464
}
465
466
return obj->str;
467
}
468
469
pdf_jsimp_obj *pdf_jsimp_from_number(pdf_jsimp *imp, double num)
470
{
471
return wrap_val(imp, JSValueMakeNumber(imp->jscore_ctx, num));
472
}
473
474
double pdf_jsimp_to_number(pdf_jsimp *imp, pdf_jsimp_obj *obj)
475
{
476
return JSValueToNumber(imp->jscore_ctx, obj->ref, NULL);
477
}
478
479
int pdf_jsimp_array_len(pdf_jsimp *imp, pdf_jsimp_obj *obj)
480
{
481
pdf_jsimp_obj *lobj = pdf_jsimp_property(imp, obj, "length");
482
int num = (int)pdf_jsimp_to_number(imp, lobj);
483
484
pdf_jsimp_drop_obj(imp, lobj);
485
486
return num;
487
}
488
489
pdf_jsimp_obj *pdf_jsimp_array_item(pdf_jsimp *imp, pdf_jsimp_obj *obj, int i)
490
{
491
return wrap_val(imp, JSObjectGetPropertyAtIndex(imp->jscore_ctx, JSValueToObject(imp->jscore_ctx, obj->ref, NULL), i, NULL));
492
}
493
494
pdf_jsimp_obj *pdf_jsimp_property(pdf_jsimp *imp, pdf_jsimp_obj *obj, char *prop)
495
{
496
JSStringRef jprop = JSStringCreateWithUTF8CString(prop);
497
JSValueRef jval = JSObjectGetProperty(imp->jscore_ctx, JSValueToObject(imp->jscore_ctx, obj->ref, NULL), jprop, NULL);
498
499
JSStringRelease(jprop);
500
501
return wrap_val(imp, jval);
502
}
503
504
void pdf_jsimp_execute(pdf_jsimp *imp, char *code)
505
{
506
JSStringRef jcode = JSStringCreateWithUTF8CString(code);
507
JSEvaluateScript(imp->jscore_ctx, jcode, NULL, NULL, 0, NULL);
508
JSStringRelease(jcode);
509
}
510
511
void pdf_jsimp_execute_count(pdf_jsimp *imp, char *code, int count)
512
{
513
char *terminated = fz_malloc(imp->ctx, count+1);
514
memcpy(terminated, code, count);
515
terminated[count] = 0;
516
pdf_jsimp_execute(imp, terminated);
517
fz_free(imp->ctx, terminated);
518
}
519
520