1 #include "fxslt_memory_TatooEngine.h"
2 #include "fxslt_memory_TatooEngine_Automaton.h"
3 #include "fxslt_memory_TatooEngine_Tree.h"
4 #include "fxslt_memory_TatooEngine_CustomBlock.h"
12 #include <caml/alloc.h>
13 #include <caml/callback.h>
14 #include <caml/memory.h>
16 #include <caml/threads.h>
22 enum { JNI_VERSION = JNI_VERSION_1_2 };
32 #define MAKE_TRAIT(T, N) \
35 static T (JNIEnv::*call)(jobject, jmethodID, va_list); \
37 T (JNIEnv::* Traits<T>::call)(jobject, jmethodID, va_list) = &JNIEnv::Call ## N ## MethodV
39 MAKE_TRAIT(jobject, Object);
40 MAKE_TRAIT(jint, Int);
41 MAKE_TRAIT(jshort, Short);
42 MAKE_TRAIT(jboolean, Boolean);
46 JNIEnv &env() throw() {
47 assert(priv::current_env);
48 return *priv::current_env;
53 scoped_env(JNIEnv *env) throw()
55 assert(not priv::current_env);
56 priv::current_env = env;
59 scoped_env(JavaVM *vm) throw(jint)
61 assert(not priv::current_env);
62 if(vm->GetEnv(reinterpret_cast<void **>(&priv::current_env), priv::JNI_VERSION) != JNI_OK)
68 assert(priv::current_env);
69 priv::current_env = NULL;
77 const char *signature;
82 const std::vector<MemberDesc> methods;
83 const std::vector<MemberDesc> fields;
90 typedef std::vector<jmethodID> Methods;
91 typedef std::vector<jfieldID> Fields;
93 static jclass get_class(const char *name) throw(jint)
96 if((c = env().FindClass(name)) == NULL)
98 if((c = static_cast<jclass>(env().NewGlobalRef(c))) == NULL)
104 get_methods(const jclass class_, const std::vector<MemberDesc> &methods) throw()
106 Methods ret(methods.size());
108 for(auto it = methods.begin(); it != methods.end(); ++it, ++i)
109 ret[i] = env().GetMethodID(class_, it->name, it->signature);
114 get_fields(const jclass class_, const std::vector<MemberDesc> &fields) throw()
116 Fields ret(fields.size());
118 for(auto it = fields.begin(); it != fields.end(); ++it, ++i)
119 ret[i] = env().GetFieldID(class_, it->name, it->signature);
125 const Methods methods;
128 Class(const ClassDesc &desc) throw(jint)
129 : class_(get_class(desc.name)), methods(get_methods(class_, desc.methods)),
130 fields(get_fields(class_, desc.fields))
133 ~Class() throw() { env().DeleteGlobalRef(class_); }
135 jboolean IsInstanceOf(jobject obj) const throw()
136 { return env().IsInstanceOf(obj, class_); }
143 static ClassDesc desc;
144 static Class *class_;
146 Object(jobject this_) throw() : this_(this_) {
147 assert(class_->IsInstanceOf(this_));
149 if (!class_->IsInstanceOf(this_)) {
151 jclass object = env().FindClass("java/lang/Object");
152 jmethodID getClass_id = env().GetMethodID(object, "getClass", "()Ljava/lang/Class;");
153 jobject oclass = env().CallObjectMethod(this_, getClass_id);
154 jclass cls = env().FindClass("java/lang/Class");
155 jmethodID getName_id = env().GetMethodID(cls, "getName", "()Ljava/lang/String;");
156 jstring name = static_cast<jstring>(env().CallObjectMethod(oclass, getName_id));
157 fprintf(stderr, "ERROR: class: %s is not an instance of %s\n", desc.name, jni::env().GetStringUTFChars(name, NULL));
160 assert(class_->IsInstanceOf(this_));
165 T call(int method_id, ...) const
168 va_start(vl, method_id);
169 T ret = (env().*priv::Traits<T>::call)(this_, class_->methods[method_id], vl);
174 static inline T static_call(jobject j, int method_id, ...) throw ()
177 va_start(vl, method_id);
178 T ret = (env().*priv::Traits<T>::call)(j, class_->methods[method_id], vl);
185 static void initialize() throw(jint) { class_ = new Class(desc); }
186 static void finalize() throw() { delete class_; class_ = NULL; }
188 static const Class& get_class() { return *class_; }
191 jni::Class *jni::Object<C>::class_ = NULL;
193 typedef priv::Integer<jint> Integer;
194 typedef priv::Integer<jshort> Short;
197 jni::ClassDesc jni::Object<Integer>::desc = {
198 "java/lang/Integer", {
199 { "intValue", "()I" },
204 jni::ClassDesc jni::Object<Short>::desc = {
206 { "shortValue", "()S" },
211 class priv::Integer: public Object<Integer<T>> {
212 enum Methods { valueID, initID };
213 typedef Object<Integer<T>> Base;
215 Integer(jobject this_) throw() : Base(this_) { }
216 Integer(jint i) throw()
217 : Base(jni::env().NewObject(Base::class_->class_, Base::class_->methods[initID], i))
219 T operator*() const throw() { return Base::template call<T>(valueID); }
224 String(const String &) = delete;
225 String& operator=(const String &) = delete;
227 mutable const char *c_str_;
231 String(jstring this_) throw() : c_str_(NULL), this_(this_) { }
232 String(String &&rhs) throw() : c_str_(rhs.c_str_), this_(rhs.this_) { rhs.c_str_ = NULL; }
235 env().ReleaseStringUTFChars(this_, c_str_);
238 const char* c_str() const throw()
243 return c_str_ = env().GetStringUTFChars(this_, NULL);
253 class MutableNodeList;
257 jni::ClassDesc jni::Object<Node>::desc = {
258 "org/w3c/dom/Node", {
259 { "getFirstChild", "()Lorg/w3c/dom/Node;" },
260 { "getNextSibling", "()Lorg/w3c/dom/Node;" },
261 { "getNodeName", "()Ljava/lang/String;" },
262 { "getNodeValue", "()Ljava/lang/String;" },
263 { "getUserData", "(Ljava/lang/String;)Ljava/lang/Object;" },
264 { "setUserData", "(Ljava/lang/String;Ljava/lang/Object;Lorg/w3c/dom/UserDataHandler;)Ljava/lang/Object;" },
265 { "getNodeType", "()S" },
266 { "getAttributes", "()Lorg/w3c/dom/NamedNodeMap;" }
270 class Node: public jni::Object<Node> {
272 getFirstChildID, getNextSiblingID, getNodeNameID, getNodeValueID, getUserDataID, setUserDataID, getNodeTypeID,
276 static jni::String *empty_key;
278 static void initialize() throw(jint)
281 empty_key = new jni::String(static_cast<jstring>(
282 jni::env().NewGlobalRef(jni::env().NewStringUTF("")) ));
284 static void finalize() throw()
286 jni::env().DeleteGlobalRef(empty_key->this_);
292 Node(jobject this_) throw() : Base(this_) { }
294 Node getFirstChild() const throw() { return Node(call<jobject>(getFirstChildID)); }
295 static inline jobject getFirstChildO(jobject obj) throw () {
296 return static_call<jobject>(obj, getFirstChildID);
298 static inline jobject getNextSiblingO(jobject obj) throw () {
299 return static_call<jobject>(obj, getNextSiblingID);
302 Node getNextSibling() const throw() { return Node(call<jobject>(getNextSiblingID)); }
303 jshort getNodeType() const throw() { return call<jshort>(getNodeTypeID); }
305 jni::String getNodeName() const throw()
306 { return jni::String(static_cast<jstring>(call<jobject>(getNodeNameID))); }
308 jni::String getNodeValue() const throw()
309 { return jni::String(static_cast<jstring>(call<jobject>(getNodeValueID))); }
312 jint getPreorder() const throw()
314 jobject data = call<jobject>(getUserDataID, empty_key->this_);
315 return *jni::Integer(data); }
316 static inline jobject getPreorderO(jobject obj) throw () {
317 return static_call<jobject>(obj, getNextSiblingID);
319 void setPreorder(jint i) const throw()
321 call<jobject>(setUserDataID, empty_key->this_, jni::Integer(i), NULL);
324 NamedNodeMap getAttributes() const throw();
326 jni::String *Node::empty_key = NULL;
328 /********** Attr *************/
330 jni::ClassDesc jni::Object<Attr>::desc = {
331 "org/w3c/dom/Attr", {
332 { "getOwnerElement", "()Lorg/w3c/dom/Element;" }
336 class Attr: public jni::Object<Attr> {
343 Attr(jobject this_) throw() : Base(this_) { }
345 Node getOwnerElement() const throw() { return Node(call<jobject>(getOwnerElementID)); }
348 /********** NodeList **********/
350 jni::ClassDesc jni::Object<NodeList>::desc = {
351 "org/w3c/dom/NodeList", {
352 { "getLength", "()I" },
353 { "item", "(I)Lorg/w3c/dom/Node;" }
357 class NodeList: public jni::Object<NodeList> {
358 enum Methods { getLengthID, itemID };
361 NodeList(jobject this_) throw() : Base(this_) { }
363 jint getLength() const throw() { return call<jint>(getLengthID); }
364 Node item(jint i) const throw() { return Node(call<jobject>(itemID, i)); }
368 /********** NamedNodeMap **********/
370 jni::ClassDesc jni::Object<NamedNodeMap>::desc = {
371 "org/w3c/dom/NamedNodeMap", {
372 { "getLength", "()I" },
373 { "item", "(I)Lorg/w3c/dom/Node;" }
377 class NamedNodeMap: public jni::Object<NamedNodeMap> {
378 enum Methods { getLengthID, itemID };
381 NamedNodeMap(jobject this_) throw() : Base(this_) { }
383 jint getLength() const throw() { return call<jint>(getLengthID); }
384 Node item(jint i) const throw() { return Node(call<jobject>(itemID, i)); }
389 jni::ClassDesc jni::Object<MutableNodeList>::desc = {
390 "fxslt/memory/MutableNodeList", {
391 { "add", "(Lorg/w3c/dom/Node;)V" },
396 class MutableNodeList: public jni::Object<MutableNodeList> {
397 enum Methods { addID, initID };
400 MutableNodeList(jobject this_) throw() : Base(this_) { }
401 MutableNodeList() throw()
402 : Base(jni::env().NewObject(class_->class_, class_->methods[initID]))
405 void add(Node n) throw() { call<jobject>(addID, n.this_); }
409 NamedNodeMap Node::getAttributes() const throw()
411 return NamedNodeMap(call<jobject>(getAttributesID));
417 jni::ClassDesc jni::Object<CustomBlock>::desc = {
418 "fxslt/memory/TatooEngine$CustomBlock", {
425 class CustomBlock: public jni::Object<CustomBlock> {
426 enum Methods { initID };
427 enum Fields { valueID };
429 static_assert(sizeof(jlong) <= sizeof(value *), "We use jlong to store pointers.");
431 value* get() const throw()
432 { return reinterpret_cast<value *>(jni::env().GetLongField(this_, class_->fields[valueID])); }
434 CustomBlock(jobject this_) throw() : Base(this_) { }
436 CustomBlock(value val) throw()
437 : Base(jni::env().NewObject(class_->class_, class_->methods[initID], new value(val)))
438 { caml_register_generational_global_root(get()); }
440 value getValue() const throw() { return *get(); }
443 JNIEXPORT void JNICALL
444 Java_fxslt_memory_TatooEngine_unregister (JNIEnv *env, jclass cls, jlong value_ptr)
446 value * vptr = reinterpret_cast<value *>(value_ptr);
447 caml_remove_generational_global_root(vptr);
451 static value *init_document;
452 static value *xpath_compile;
453 static value *auto_evaluate;
455 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *)
458 jni::scoped_env se(vm);
459 jni::Integer::initialize();
462 NodeList::initialize();
463 NamedNodeMap::initialize();
464 MutableNodeList::initialize();
465 CustomBlock::initialize();
467 char *argv[] = { NULL };
470 init_document = caml_named_value("init_document"); assert(init_document);
471 xpath_compile = caml_named_value("xpath_compile"); assert(xpath_compile);
472 auto_evaluate = caml_named_value("auto_evaluate"); assert(auto_evaluate);
473 caml_release_runtime_system();
479 return jni::priv::JNI_VERSION;
482 JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *)
485 jni::scoped_env se(vm);
486 MutableNodeList::finalize();
487 NamedNodeMap::finalize();
488 NodeList::finalize();
491 jni::Integer::finalize();
494 fprintf(stderr, "Critical error unloading shared library.\n");
498 static jobject extract(const value &val)
499 { return reinterpret_cast<jobject>(val); }
501 static value pack(jobject obj)
503 // static_assert(sizeof(uintptr_t) <= sizeof(long), "We need long to hold pointers.");
505 // uintptr_t p = reinterpret_cast<uintptr_t>(obj);
506 // assert(! (p & 1));
507 return reinterpret_cast<value> (obj);
511 JNIEXPORT jobject JNICALL
512 Java_fxslt_memory_TatooEngine_init_1document(JNIEnv *env, jclass TatooEngine, jobject node, jint i)
517 jni::scoped_env se(env);
518 node = jni::env().NewGlobalRef(node);
519 val = caml_callback2(*init_document, pack(node), Val_int(i));
520 auto t = jni::env().NewGlobalRef(CustomBlock(val).this_);
521 CAMLreturnT(auto, t);
523 fprintf(stderr, "Critical error while initializing the document.\n");
524 CAMLreturnT(jobject, NULL);
530 JNIEXPORT jobject JNICALL
531 Java_fxslt_memory_TatooEngine_compile(JNIEnv *env, jclass TatooEngine, jstring xpath)
537 jni::scoped_env se(env);
539 val = caml_callback(*xpath_compile, caml_copy_string(jni::String(xpath).c_str()));
540 auto a = CustomBlock(val).this_;
541 a = jni::env().NewGlobalRef(a);
542 CAMLreturnT(auto, a);
545 fprintf(stderr, "Critical error while compiling.\n");
546 CAMLreturnT(jobject, NULL);
550 JNIEXPORT jobject JNICALL
551 Java_fxslt_memory_TatooEngine_evaluate(JNIEnv *env, jclass TatooEngine,
552 jobject automaton, jobject tree, jobject node_list)
555 CAMLlocal4(res, vauto, vtree, vnl);
558 jni::scoped_env se(env);
559 vauto = CustomBlock(automaton).getValue();
560 vtree = CustomBlock(tree).getValue();
561 vnl = pack(node_list);
563 res = caml_callback3(*auto_evaluate, vauto, vtree, vnl);
564 CAMLreturnT(auto, extract(res));
566 fprintf(stderr, "Critical error while evaluating.\n");
567 CAMLreturnT(jobject, NULL);
571 #define GR (node) (jni::env().NewGlobalRef((node)))
574 #define CHECK_EXCEPTION() do { \
575 if (jni::env().ExceptionCheck() == JNI_TRUE) { \
576 jni::env().ExceptionDescribe(); \
581 #define CHECK_EXCEPTION()
585 CAMLprim value node_getFirstChild(value node)
589 CAMLreturn(pack(Node::getFirstChildO(extract(node))));
590 //CAMLreturn(pack(Node(extract(node)).getFirstChild().this_));
593 CAMLprim value node_getNextSibling(value node)
596 CAMLreturn(pack(Node::getNextSiblingO(extract(node))));
597 //CAMLreturn(pack(Node(extract(node)).getNextSibling().this_));
600 CAMLprim value node_getNodeType(value node)
603 CAMLreturn(Val_int(Node(extract(node)).getNodeType())); }
605 CAMLprim value node_getNodeName(value node)
608 jstring obj = Node(extract(node)).getNodeName().this_;
609 value cstr = caml_copy_string(jni::env().GetStringUTFChars(obj, NULL));
613 CAMLprim value node_getPreorder(value node)
616 CAMLreturn(Val_int(Node(extract(node)).getPreorder()));
619 CAMLprim value node_setPreorder(value node, value i)
623 Node(extract(node)).setPreorder(Int_val(i));
624 CAMLreturn(Val_unit);
627 CAMLprim value print_runtime_class(value o)
631 jclass object = jni::env().FindClass("java/lang/Object");
632 jmethodID getClass_id = jni::env().GetMethodID(object, "getClass", "()Ljava/lang/Class;");
633 jobject oclass = jni::env().CallObjectMethod(extract(o), getClass_id);
634 jclass cls = jni::env().FindClass("java/lang/Class");
635 jmethodID getName_id = jni::env().GetMethodID(cls, "getName", "()Ljava/lang/String;");
636 jstring name = static_cast<jstring>(jni::env().CallObjectMethod(oclass, getName_id));
637 fprintf(stderr, "CLASS OF ATTTRIBUTE IS %s \n", jni::env().GetStringUTFChars(name, NULL));
639 CAMLreturn(Val_unit);
641 CAMLprim value attr_getOwnerElement(value node)
645 auto attr = Attr(extract(node));
646 CAMLreturn(pack(attr.getOwnerElement().this_));
649 CAMLprim value node_getAttributes(value node)
652 CAMLreturn(pack(Node(extract(node)).getAttributes().this_)); }
654 CAMLprim value nodelist_getLength(value list)
657 CAMLreturn(Val_int(NodeList(extract(list)).getLength())); }
659 CAMLprim value nodelist_item(value list, value idx)
661 CAMLparam2(list, idx);
663 CAMLreturn(pack(NodeList(extract(list)).item(Long_val(idx)).this_));
666 CAMLprim value nodelist_new(value list)
668 auto l = jni::env().NewGlobalRef(MutableNodeList().this_);
672 CAMLprim value nodelist_add(value list, value node)
674 CAMLparam2(list, node);
675 MutableNodeList(extract(list)).add(Node(extract(node)));
679 CAMLprim value namednodemap_getLength(value list)
680 { CAMLparam1(list); CAMLreturn(Val_int(NamedNodeMap(extract(list)).getLength())); }
682 CAMLprim value namednodemap_item(value list, value idx)
684 CAMLparam2(list, idx);
685 CAMLreturn(pack(NamedNodeMap(extract(list)).item(Long_val(idx)).this_));
688 CAMLprim value getNull(value unit) { CAMLparam1(unit); CAMLreturn((value) NULL); }
690 CAMLprim value dereference_object (value obj)
693 jni::env().DeleteGlobalRef(reinterpret_cast<jobject>(obj));
694 CAMLreturn(Val_unit);