/* * 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. */ extern "C" { #include "fitz-internal.h" #include "mupdf-internal.h" #include "pdf_jsimp_cpp.h" } #include #include #include using namespace v8; using namespace std; struct PDFJSImp; /* Object we pass to FunctionTemplate::New, which v8 passes back to us in * callMethod, allowing us to call our client's, passed-in method. */ struct PDFJSImpMethod { PDFJSImp *imp; pdf_jsimp_method *meth; PDFJSImpMethod(PDFJSImp *imp, pdf_jsimp_method *meth) : imp(imp), meth(meth) {} }; /* Object we pass to ObjectTemplate::SetAccessor, which v8 passes back to us in * setProp and getProp, allowing us to call our client's, passed-in set/get methods. */ struct PDFJSImpProperty { PDFJSImp *imp; pdf_jsimp_getter *get; pdf_jsimp_setter *set; PDFJSImpProperty(PDFJSImp *imp, pdf_jsimp_getter *get, pdf_jsimp_setter *set) : imp(imp), get(get), set(set) {} }; /* Internal representation of the pdf_jsimp_type object */ struct PDFJSImpType { PDFJSImp *imp; Persistent templ; pdf_jsimp_dtr *dtr; vector methods; vector properties; PDFJSImpType(PDFJSImp *imp, pdf_jsimp_dtr *dtr): imp(imp), dtr(dtr) { HandleScope scope; templ = Persistent::New(ObjectTemplate::New()); templ->SetInternalFieldCount(1); } ~PDFJSImpType() { vector::iterator mit; for (mit = methods.begin(); mit < methods.end(); mit++) delete *mit; vector::iterator pit; for (pit = properties.begin(); pit < properties.end(); pit++) delete *pit; templ.Dispose(); } }; /* Info via which we destroy the client side part of objects that * v8 garbage collects */ struct PDFJSImpGCObj { Persistent pobj; PDFJSImpType *type; PDFJSImpGCObj(Handle obj, PDFJSImpType *type): type(type) { pobj = Persistent::New(obj); } ~PDFJSImpGCObj() { pobj.Dispose(); } }; /* Internal representation of the pdf_jsimp object */ struct PDFJSImp { fz_context *ctx; void *jsctx; Persistent context; vector types; set gclist; PDFJSImp(fz_context *ctx, void *jsctx) : ctx(ctx), jsctx(jsctx) { HandleScope scope; context = Persistent::New(Context::New()); } ~PDFJSImp() { HandleScope scope; /* Tell v8 our context will not be used again */ context.Dispose(); /* Unlink and destroy all the objects that v8 has yet to gc */ set::iterator oit; for (oit = gclist.begin(); oit != gclist.end(); oit++) { (*oit)->pobj.ClearWeak(); /* So that gcCallback wont get called */ PDFJSImpType *vType = (*oit)->type; Local owrap = Local::Cast((*oit)->pobj->GetInternalField(0)); vType->dtr(vType->imp->jsctx, owrap->Value()); delete *oit; } vector::iterator it; for (it = types.begin(); it < types.end(); it++) delete *it; } }; /* Internal representation of the pdf_jsimp_obj object */ class PDFJSImpObject { Persistent pobj; String::Utf8Value *utf8; public: PDFJSImpObject(Handle obj): utf8(NULL) { pobj = Persistent::New(obj); } PDFJSImpObject(const char *str): utf8(NULL) { pobj = Persistent::New(String::New(str)); } PDFJSImpObject(double num): utf8(NULL) { pobj = Persistent::New(Number::New(num)); } ~PDFJSImpObject() { delete utf8; pobj.Dispose(); } int type() { if (pobj->IsNull()) return JS_TYPE_NULL; else if (pobj->IsString() || pobj->IsStringObject()) return JS_TYPE_STRING; else if (pobj->IsNumber() || pobj->IsNumberObject()) return JS_TYPE_NUMBER; else if (pobj->IsArray()) return JS_TYPE_ARRAY; else if (pobj->IsBoolean() || pobj->IsBooleanObject()) return JS_TYPE_BOOLEAN; else return JS_TYPE_UNKNOWN; } char *toString() { delete utf8; utf8 = new String::Utf8Value(pobj); return **utf8; } double toNumber() { return pobj->NumberValue(); } Handle toValue() { return pobj; } }; extern "C" fz_context *pdf_jsimp_ctx_cpp(pdf_jsimp *imp) { return reinterpret_cast(imp)->ctx; } extern "C" const char *pdf_new_jsimp_cpp(fz_context *ctx, void *jsctx, pdf_jsimp **imp) { Locker lock; *imp = reinterpret_cast(new PDFJSImp(ctx, jsctx)); return NULL; } extern "C" const char *pdf_drop_jsimp_cpp(pdf_jsimp *imp) { Locker lock; delete reinterpret_cast(imp); return NULL; } extern "C" const char *pdf_jsimp_new_type_cpp(pdf_jsimp *imp, pdf_jsimp_dtr *dtr, pdf_jsimp_type **type) { Locker lock; PDFJSImp *vImp = reinterpret_cast(imp); PDFJSImpType *vType = new PDFJSImpType(vImp, dtr); vImp->types.push_back(vType); *type = reinterpret_cast(vType); return NULL; } extern "C" const char *pdf_jsimp_drop_type_cpp(pdf_jsimp *imp, pdf_jsimp_type *type) { /* Types are recorded and destroyed as part of PDFJSImp */ return NULL; } static Handle callMethod(const Arguments &args) { HandleScope scope; Local mwrap = Local::Cast(args.Data()); PDFJSImpMethod *m = (PDFJSImpMethod *)mwrap->Value(); Local self = args.Holder(); Local owrap; void *nself = NULL; if (self->InternalFieldCount() > 0) { owrap = Local::Cast(self->GetInternalField(0)); nself = owrap->Value(); } int c = args.Length(); PDFJSImpObject **native_args = new PDFJSImpObject*[c]; for (int i = 0; i < c; i++) native_args[i] = new PDFJSImpObject(args[i]); PDFJSImpObject *obj = reinterpret_cast(pdf_jsimp_call_method(reinterpret_cast(m->imp), m->meth, m->imp->jsctx, nself, c, reinterpret_cast(native_args))); Handle val; if (obj) val = obj->toValue(); delete obj; for (int i = 0; i < c; i++) delete native_args[i]; delete native_args; return scope.Close(val); } extern "C" const char *pdf_jsimp_addmethod_cpp(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_method *meth) { Locker lock; PDFJSImpType *vType = reinterpret_cast(type); HandleScope scope; PDFJSImpMethod *pmeth = new PDFJSImpMethod(vType->imp, meth); vType->templ->Set(String::New(name), FunctionTemplate::New(callMethod, External::New(pmeth))); vType->methods.push_back(pmeth); return NULL; } static Handle getProp(Local property, const AccessorInfo &info) { HandleScope scope; Local pwrap = Local::Cast(info.Data()); PDFJSImpProperty *p = reinterpret_cast(pwrap->Value()); Local self = info.Holder(); Local owrap; void *nself = NULL; if (self->InternalFieldCount() > 0) { Local val = self->GetInternalField(0); if (val->IsExternal()) { owrap = Local::Cast(val); nself = owrap->Value(); } } PDFJSImpObject *obj = reinterpret_cast(pdf_jsimp_call_getter(reinterpret_cast(p->imp), p->get, p->imp->jsctx, nself)); Handle val; if (obj) val = obj->toValue(); delete obj; return scope.Close(val); } static void setProp(Local property, Local value, const AccessorInfo &info) { HandleScope scope; Local wrap = Local::Cast(info.Data()); PDFJSImpProperty *p = reinterpret_cast(wrap->Value()); Local self = info.Holder(); Local owrap; void *nself = NULL; if (self->InternalFieldCount() > 0) { owrap = Local::Cast(self->GetInternalField(0)); nself = owrap->Value(); } PDFJSImpObject *obj = new PDFJSImpObject(value); pdf_jsimp_call_setter(reinterpret_cast(p->imp), p->set, p->imp->jsctx, nself, reinterpret_cast(obj)); delete obj; } extern "C" const char *pdf_jsimp_addproperty_cpp(pdf_jsimp *imp, pdf_jsimp_type *type, char *name, pdf_jsimp_getter *get, pdf_jsimp_setter *set) { Locker lock; PDFJSImpType *vType = reinterpret_cast(type); HandleScope scope; PDFJSImpProperty *prop = new PDFJSImpProperty(vType->imp, get, set); vType->templ->SetAccessor(String::New(name), getProp, setProp, External::New(prop)); vType->properties.push_back(prop); return NULL; } extern "C" const char *pdf_jsimp_set_global_type_cpp(pdf_jsimp *imp, pdf_jsimp_type *type) { Locker lock; PDFJSImp *vImp = reinterpret_cast(imp); PDFJSImpType *vType = reinterpret_cast(type); HandleScope scope; vImp->context = Persistent::New(Context::New(NULL, vType->templ)); return NULL; } static void gcCallback(Persistent val, void *parm) { PDFJSImpGCObj *gco = reinterpret_cast(parm); PDFJSImpType *vType = gco->type; HandleScope scope; Persistent obj = Persistent::Cast(val); Local owrap = Local::Cast(obj->GetInternalField(0)); vType->dtr(vType->imp->jsctx, owrap->Value()); vType->imp->gclist.erase(gco); delete gco; /* Disposes of the persistent handle */ } extern "C" const char *pdf_jsimp_new_obj_cpp(pdf_jsimp *imp, pdf_jsimp_type *type, void *natobj, pdf_jsimp_obj **robj) { Locker lock; PDFJSImpType *vType = reinterpret_cast(type); HandleScope scope; Local obj = vType->templ->NewInstance(); obj->SetInternalField(0, External::New(natobj)); /* Arrange for destructor to be called on the client-side object * when the v8 object is garbage collected */ if (vType->dtr) { /* Wrap obj in a PDFJSImpGCObj, which takes a persistent handle to * obj, and stores its type with it. The persistent handle tells v8 * it cannot just destroy obj leaving the client-side object hanging */ PDFJSImpGCObj *gco = new PDFJSImpGCObj(obj, vType); /* Keep the wrapped object in a list, so that we can take back control * of destroying client-side objects when shutting down this context */ vType->imp->gclist.insert(gco); /* Tell v8 that it can destroy the persistent handle to obj when it has * no further need for it, but it must inform us via gcCallback */ gco->pobj.MakeWeak(gco, gcCallback); } *robj = reinterpret_cast(new PDFJSImpObject(obj)); return NULL; } extern "C" const char *pdf_jsimp_drop_obj_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj) { Locker lock; delete reinterpret_cast(obj); return NULL; } extern "C" const char *pdf_jsimp_to_type_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, int *type) { Locker lock; *type = reinterpret_cast(obj)->type(); return NULL; } extern "C" const char *pdf_jsimp_from_string_cpp(pdf_jsimp *imp, char *str, pdf_jsimp_obj **obj) { Locker lock; *obj = reinterpret_cast(new PDFJSImpObject(str)); return NULL; } extern "C" const char *pdf_jsimp_to_string_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, char **str) { Locker lock; *str = reinterpret_cast(obj)->toString(); return NULL; } extern "C" const char *pdf_jsimp_from_number_cpp(pdf_jsimp *imp, double num, pdf_jsimp_obj **obj) { Locker lock; *obj = reinterpret_cast(new PDFJSImpObject(num)); return NULL; } extern "C" const char *pdf_jsimp_to_number_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, double *num) { Locker lock; *num = reinterpret_cast(obj)->toNumber(); return NULL; } extern "C" const char *pdf_jsimp_array_len_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, int *len) { Locker lock; Local jsobj = reinterpret_cast(obj)->toValue()->ToObject(); Local arr = Local::Cast(jsobj); *len = arr->Length(); return NULL; } extern "C" const char *pdf_jsimp_array_item_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, int i, pdf_jsimp_obj **item) { Locker lock; Local jsobj = reinterpret_cast(obj)->toValue()->ToObject(); *item = reinterpret_cast(new PDFJSImpObject(jsobj->Get(Number::New(i)))); return NULL; } extern "C" const char *pdf_jsimp_property_cpp(pdf_jsimp *imp, pdf_jsimp_obj *obj, char *prop, pdf_jsimp_obj **pobj) { Locker lock; Local jsobj = reinterpret_cast(obj)->toValue()->ToObject(); *pobj = reinterpret_cast(new PDFJSImpObject(jsobj->Get(String::New(prop)))); return NULL; } extern "C" const char *pdf_jsimp_execute_cpp(pdf_jsimp *imp, char *code) { Locker lock; PDFJSImp *vImp = reinterpret_cast(imp); HandleScope scope; Context::Scope context_scope(vImp->context); Handle