├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── binding.gyp ├── examples ├── browser.js ├── clutter.js ├── dump-clutter.js ├── dump-midgard.js ├── gtk.js ├── gtk_test.js ├── guitartuner.coffee ├── libxml2.js ├── midgard.js ├── multiple_test.js ├── notify.js ├── notify_test.js └── webkit.js ├── gir.js ├── package.json ├── src ├── arguments.cc ├── arguments.h ├── function.cc ├── function.h ├── init.cc ├── namespace_loader.cc ├── namespace_loader.h ├── types │ ├── function_type.cc │ ├── function_type.h │ ├── object.cc │ ├── object.h │ ├── struct.cc │ └── struct.h ├── util.cc ├── util.h ├── values.cc └── values.h └── tests ├── TestGIR.js ├── midgard ├── bootstrap.sh ├── midgard_connection.js ├── test_000_config.js ├── test_300_storage.js ├── test_400_timestamp.js ├── test_401_boxed_timestamp.js ├── test_500_object_metadata.js ├── test_510_object_crud.js ├── test_550_signals.js ├── test_600_user.js ├── test_SQLITE.conf ├── test_data │ ├── MidgardObjects.xml │ ├── midgard_auth_types.xml │ └── schema │ │ ├── phpcr_mixin_schemas.xml │ │ ├── phpcr_schemas.xml │ │ └── test_book_crud.xml ├── test_query_100_column.js ├── test_query_200_sql_query_select_data.js ├── test_query_300_sql_query_result.js └── test_query_400_sql_query_result_constraints.js ├── test_arguments_directions.js ├── test_object_constructor.js ├── test_object_property.js ├── test_returned_value.js ├── test_structure_constructor.js └── travis_midgard.sh /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .lock-wscript 3 | node_modules/ 4 | npm-debug.log 5 | tests/midgard/test_data/*.db 6 | 7 | # debugging 8 | .gdb_history 9 | peda-session-* 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.6 4 | - 0.8 5 | 6 | before_install: tests/travis_midgard.sh 7 | 8 | script: xvfb-run npm test 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Piotr Pokora and Tim Caswell 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-gir 2 | 3 | Node-gir is Node.js bindings to [GObject Introspection](https://live.gnome.org/GObjectIntrospection/) making it possible to make automatic and dynamic calls to any library that has GI annotations installed. This includes most libraries from the [GNOME project](http://developer.gnome.org/). 4 | 5 | This will make it possible to script a GNOME desktop system entirely from node much in the way it's done today with Seed, GJS or pygtk. It also allows using GNOME libraries in Node.js applications. With it you can also write the performance-intensive parts of your applications [in Vala](https://github.com/antono/vala-object) and call them from Node.js and other languages. 6 | 7 | ## Installation 8 | 9 | You need GObject Introspection library to be installed. On a Debian-like system this would be handled by: 10 | 11 | $ sudo apt-get install libgirepository1.0-dev 12 | 13 | Then just build node-gir with: 14 | 15 | $ npm install gir 16 | 17 | ## Testing 18 | 19 | The node-gir repository comes with a set of tests that utilize the Midgard2 library to test against. You need also that installed, and then run: 20 | 21 | $ npm test 22 | 23 | Travis is used for Continous Integration: 24 | 25 | [![Build Status](https://secure.travis-ci.org/piotras/node-gir.png?branch=master)](http://travis-ci.org/piotras/node-gir) 26 | 27 | ## Architecture 28 | 29 | The following graph shows all the parts and how they work together. The only 30 | missing part is node bindings to libgirepository. Hence this project. 31 | 32 | BUILD TIME: 33 | 34 | +-----------------------------------------------------------+ 35 | | foo.c | 36 | | foo.h | 37 | | | 38 | | Library sources, with type annotations | 39 | +-----------------------------------------------------------+ 40 | | | 41 | gcc g-ir-scanner 42 | | | 43 | | V 44 | | +------------------------+ 45 | | | Foo.gir | 46 | | | | 47 | | | .gir | 48 | | | | 49 | | | XML file | 50 | | | | 51 | | | Invocation information | 52 | | | Required .gir files | 53 | | | API docs | 54 | | | | 55 | | +------------------------+ 56 | | | 57 | | g-ir-compiler 58 | | | 59 | DEPLOYMENT TIME: | 60 | | | 61 | V V 62 | +-----------------------------+ +---------------------------+ 63 | | libfoo.so | | Foo.typelib | 64 | | | | | 65 | | | | Binary version of the | 66 | | ELF file | | invocation info and | 67 | | | | required .typelib files | 68 | | Machine code, plus | +---------------------------+ 69 | | dynamic linkage information | A 70 | | (DWARF debug data, etc) | | 71 | +-----------------------------+ | 72 | A | 73 | | +---------------------------+ 74 | | | libgirepository.so | 75 | +-----------+ | | 76 | | libffi.so | | Can read typelibs and | 77 | | | | present them in a | 78 | +-----------+ | libffi-based way | 79 | A | | 80 | | +---------------------------+ 81 | | A 82 | | | 83 | | +------------+ 84 | +--------------------------| node-gir | 85 | | | 86 | +--------->+------------+ 87 | | 88 | +------------------+ 89 | | NodeJS | 90 | +------------------+ 91 | 92 | ## Why not use Seed or GJS 93 | 94 | Because they are nice, but not what I'm looking for. Node is really popular and 95 | it would be nice to be able to use it for desktop tools and applications. 96 | 97 | ## Implementation Notes 98 | 99 | Here are some links and notes as I try to figure out how to do this. 100 | 101 | - 102 | - 103 | 104 | ## API Ideas 105 | 106 | Some of these ideas will go in this binding and some will go in nice wrappers 107 | that use it. I'll know more as we progress. 108 | 109 | - Use `camelCase` for methods that are bound to look JavaScripty. 110 | - Use `.on(name, callback)` to attach signals. 111 | - Keep the same constructor style used by Seed and GJS 112 | - Make the module system as node-like as possible. 113 | 114 | ## Things which work 115 | 116 | - All classes get created 117 | - classes get inherited 118 | - interface methods are inherited 119 | - A class has lists of all its properties, methods, signals, vfuncs and fields 120 | - C structures are propagated as objects (fields are properties) 121 | - Both methods and static method can be called 122 | - You can create a class 123 | - functions can be called (but it does not work so well with 'out' arguments which should be set as returned value) 124 | - GError is propagated as generic exception 125 | - property values can be set/get 126 | - events can be watched 127 | - flags, enums etc are set 128 | 129 | ## Things which dont work (correct) 130 | 131 | - Conversion between a v8 value and a GValue/GArgument is veeeery buggy (but everything needs it so most things are buggy) 132 | - No support for libev/libuv; glib is using its own stuff (gst.main()) 133 | - There is no good way to delete an object (memory management sucks at all) 134 | - types/function.cc need a rewrite 135 | - GError should be propagated as derived classes depending on GError domain 136 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'girepository', 5 | 'sources': [ 6 | 'src/init.cc', 7 | 'src/util.cc', 8 | 'src/namespace_loader.cc', 9 | 'src/arguments.cc', 10 | 'src/function.cc', 11 | 'src/values.cc', 12 | 'src/types/object.cc', 13 | 'src/types/struct.cc', 14 | 'src/types/function_type.cc' 15 | ], 16 | 'conditions': [ 17 | ['OS=="linux"', 18 | { 19 | 'libraries': [ 20 | ' 7 | gir = require '../gir' 8 | gtk = gir.load 'Gtk', '3.0' 9 | gst = gir.load 'Gst', '0.10' 10 | 11 | gtk.init 0 12 | gst.init 0 13 | 14 | guitarwindow = new gtk.Window 15 | type: gtk.WindowType.toplevel 16 | title: "Node.js Guitar Tuner" 17 | # border_width: 100 18 | 19 | guitarwindow.on 'destroy', -> 20 | gtk.mainQuit() 21 | process.exit() 22 | 23 | guitar_box = new gtk.ButtonBox 24 | 25 | playSound = (frequency) -> 26 | console.log frequency 27 | pipeline = new gst.Pipeline 28 | name: 'note' 29 | source = new gst.ElementFactory.make "audiotestsrc", "source" 30 | sink = new gst.ElementFactory.make "autoaudiosink", "output" 31 | source.set_property "freq", frequency 32 | pipeline.add source 33 | pipeline.add sink 34 | source.link sink 35 | pipeline.set_state gst.State.PLAYING 36 | 37 | #setTimeout -> 38 | # pipeline.set_state gst.State.PAUSED 39 | #, 500 40 | 41 | addButton = (tune, freq) -> 42 | button = new gtk.Button 43 | label: tune 44 | guitar_box.add button 45 | 46 | button.on 'clicked', -> 47 | playSound freq 48 | 49 | tunes = 50 | E: 369.23 51 | A: 440 52 | D: 587.33 53 | G: 783.99 54 | B: 987.77 55 | e: 1318.5 56 | 57 | addButton tune, freq for tune, freq of tunes 58 | guitarwindow.add guitar_box 59 | guitar_box.show_all() 60 | 61 | guitarwindow.show() 62 | 63 | gtk.main() 64 | -------------------------------------------------------------------------------- /examples/libxml2.js: -------------------------------------------------------------------------------- 1 | var gir = require('../gir') 2 | , libxml2 = exports.gtk = gir.load('libxml2'); 3 | -------------------------------------------------------------------------------- /examples/midgard.js: -------------------------------------------------------------------------------- 1 | var gir = require('../gir') 2 | , Midgard = module.exports = gir.load('Midgard'); 3 | -------------------------------------------------------------------------------- /examples/multiple_test.js: -------------------------------------------------------------------------------- 1 | var gtk = require('./gtk') 2 | , notify = require('./notify'); 3 | 4 | notify.init('a'); 5 | gtk.init(0, null); 6 | -------------------------------------------------------------------------------- /examples/notify.js: -------------------------------------------------------------------------------- 1 | var gir = require('../gir') 2 | , notify = module.exports = gir.load('Notify'); 3 | -------------------------------------------------------------------------------- /examples/notify_test.js: -------------------------------------------------------------------------------- 1 | //docs: http://developer.gnome.org/libnotify/0.7/NotifyNotification.html 2 | var notify = require('./notify'); 3 | 4 | console.log(notify.init('notify_test.js sample application')); 5 | 6 | var n = new notify.Notification({'summary':'a'}); 7 | 8 | n.update('Notify Test', 'This is a test notification message via Node.JS.'); 9 | n.show(); 10 | 11 | setTimeout( 12 | function () { 13 | n.update('Notification Update', 'The status has been updated!'); 14 | n.show(); 15 | setTimeout( 16 | function () { 17 | n.update('Ethan', 'This message will self-destruct in 5 seconds.'); 18 | n.show(); 19 | setTimeout( 20 | function () { 21 | n.close(); 22 | console.log('Adios!'); 23 | }, 5000 24 | ); 25 | }, 4000 26 | ); 27 | }, 3000 28 | ); 29 | -------------------------------------------------------------------------------- /examples/webkit.js: -------------------------------------------------------------------------------- 1 | var gir = require('../gir') 2 | , webkit = module.exports = gir.load('WebKit', '3.0'); 3 | -------------------------------------------------------------------------------- /gir.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Example use of this module: 3 | * var gir = require('./path/to/gir.js') 4 | * , gtk = gir.load('Gtk', '3.0'); 5 | **/ 6 | // import gir library and EventEmitter 7 | var gir = module.exports = require('./build/Release/girepository'), 8 | EventEmitter = require('events').EventEmitter; 9 | 10 | /******************************************************************************/ 11 | 12 | /* BEGIN HELPERS */ 13 | 14 | /** 15 | * Adopted from jquery's extend method. Under the terms of MIT License. 16 | * 17 | * http://code.jquery.com/jquery-1.4.2.js 18 | * 19 | * Modified by Brian White to use Array.isArray instead of the custom isArray 20 | * method. 21 | */ 22 | function extend() { 23 | // copy reference to target object 24 | var target = arguments[0] || {}, 25 | i = 1, 26 | length = arguments.length, 27 | deep = false, 28 | options, 29 | name, 30 | src, 31 | copy; 32 | 33 | // Handle a deep copy situation 34 | if (typeof target === "boolean") { 35 | deep = target; 36 | target = arguments[1] || {}; 37 | // skip the boolean and the target 38 | i = 2; 39 | } 40 | 41 | // Handle case when target is a string or something (possible in deep copy) 42 | if (typeof target !== "object" && !typeof target === 'function') 43 | target = {}; 44 | 45 | var isPlainObject = function(obj) { 46 | // Must be an Object. 47 | // Because of IE, we also have to check the presence of the constructor 48 | // property. 49 | // Make sure that DOM nodes and window objects don't pass through, as well 50 | if (!obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval) 51 | return false; 52 | 53 | var has_own_constructor = hasOwnProperty.call(obj, "constructor"); 54 | var has_is_prop_of_method = hasOwnProperty.call(obj.constructor.prototype, 55 | "isPrototypeOf"); 56 | // Not own constructor property must be Object 57 | if (obj.constructor && !has_own_constructor && !has_is_prop_of_method) 58 | return false; 59 | 60 | // Own properties are enumerated firstly, so to speed up, 61 | // if last one is own, then all properties are own. 62 | 63 | var last_key; 64 | for (key in obj) 65 | last_key = key; 66 | 67 | return typeof last_key === "undefined" || hasOwnProperty.call(obj, last_key); 68 | }; 69 | 70 | 71 | for (; i < length; i++) { 72 | // Only deal with non-null/undefined values 73 | if ((options = arguments[i]) !== null) { 74 | // Extend the base object 75 | for (name in options) { 76 | src = target[name]; 77 | copy = options[name]; 78 | 79 | // Prevent never-ending loop 80 | if (target === copy) 81 | continue; 82 | 83 | // Recurse if we're merging object literal values or arrays 84 | if (deep && copy && (isPlainObject(copy) || Array.isArray(copy))) { 85 | var clone = src && (isPlainObject(src) || Array.isArray(src) ? src : (Array.isArray(copy) ? [] : {})); 86 | 87 | // Never move original objects, clone them 88 | target[name] = extend(deep, clone, copy); 89 | 90 | // Don't bring in undefined values 91 | } else if (typeof copy !== "undefined") 92 | target[name] = copy; 93 | } 94 | } 95 | } 96 | 97 | // Return the modified object 98 | return target; 99 | }; 100 | 101 | /** 102 | * Copied from jQuery. Under the terms of MIT or GPLv2 License. 103 | * http://code.jquery.com/jquery-1.7.1.js 104 | */ 105 | function merge(first, second) { 106 | var i = first.length, 107 | j = 0; 108 | 109 | if (typeof second.length === "number") { 110 | for (var l = second.length; j < l; j++) { 111 | first[i++] = second[j]; 112 | } 113 | 114 | } else { 115 | while (second[j] !== undefined) { 116 | first[i++] = second[j++]; 117 | } 118 | } 119 | 120 | first.length = i; 121 | 122 | return first; 123 | } 124 | 125 | /** 126 | * Copied from jQuery. Under the terms of MIT or GPLv2 License. 127 | * http://code.jquery.com/jquery-1.7.1.js 128 | * 129 | * Modified by David Ball to work outside the jQuery environment. Removed 130 | * reference to jQuery.isWindow() and jQuery.type(). Modified jQuery.merge() to 131 | * use local scope. Applied logic from jQuery.type() since there is no DOM. 132 | * Changed push to Array.prototype.push. 133 | */ 134 | function makeArray(array, results) { 135 | var ret = results || []; 136 | 137 | if (array != null) { 138 | // The window, strings (and functions) also have 'length' 139 | // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 140 | var type = array == null ? String(obj) : "object"; 141 | 142 | if (array.length == null || type === "string" || type === "function" || type === "regexp") { 143 | Array.prototype.push.call(ret, array); 144 | } else { 145 | merge(ret, array); 146 | } 147 | } 148 | 149 | return ret; 150 | } 151 | 152 | /* END HELPERS */ 153 | 154 | /******************************************************************************/ 155 | 156 | /* BEGIN LOGIC */ 157 | 158 | //save default module routines 159 | gir._gir_baseLoad = gir.load; 160 | 161 | //create callable method object 162 | function CallableMethod(methodName) { 163 | //the internal function does all the hard work 164 | var invocation = function() { 165 | var args = Array.prototype.slice.call(arguments); 166 | if (args == undefined) args = new Array(); 167 | for (var i = args.length; i > 0; i--) 168 | args[i] = args[i - 1]; 169 | args[0] = methodName; 170 | //call the method on the gir provided object 171 | this.apply(this, args); 172 | }; 173 | return invocation; 174 | } 175 | 176 | //override default loader 177 | gir.load = function() { 178 | //load gir module 179 | var obj = gir['_gir_baseLoad'].apply(this, Array.prototype.slice.call(arguments)); 180 | 181 | //check for error 182 | if (!obj) return obj; 183 | 184 | //TODO: consider storing loaded module gir somewhere now so that it can be unloaded later ? 185 | 186 | //for each object within the loaded gir module: 187 | // task 1. figure out which loaded objects can trigger events, and add EventEmitter as needed 188 | // task 2. make method names callable methods 189 | for (var subobj in obj) { 190 | //task 1: add EventEmitter as needed 191 | //determine whether eventable 192 | var eventable = obj[subobj].__signals__ != undefined && makeArray(obj[subobj].__signals__).length > 0; 193 | if (eventable) { 194 | //combine EventEmitter logic with eventable gir objects 195 | extend(true, obj[subobj].prototype, EventEmitter.prototype); 196 | //check for prop __watch_signal__, if found, override EventEmitter.on() 197 | if (obj[subobj].prototype['__watch_signal__'] != undefined) { 198 | obj[subobj].prototype._baseEventEmitter_on = obj[subobj].prototype.on; 199 | obj[subobj].prototype.on = function() { 200 | //tell gir loaded object to listen for the signal 201 | this.__watch_signal__(arguments[0]); 202 | //dispatch normally 203 | this._baseEventEmitter_on(arguments[0], arguments[1]); 204 | }; 205 | } 206 | } 207 | 208 | //task 2: expose object methods to objects and make them callable 209 | for (var prop in obj[subobj]) { 210 | switch (prop) { 211 | case '__methods__': 212 | for (var method_offset in obj[subobj][prop]) { 213 | var method_name = obj[subobj][prop][method_offset]; 214 | //debug:console.log(subobj + '.' + method_name + '() discovered'); 215 | //add method handler to object if possible 216 | if (obj[subobj].prototype[method_name] != undefined) {} //debug:console.warn("[node-gir] " + subobj + " object provides it's own " + method_name + " method. Not replacing existing method. :-("); 217 | else 218 | obj[subobj].prototype[method_name] = CallableMethod(method_name); 219 | } 220 | break; 221 | } 222 | } 223 | //console.log(subobj, obj[subobj]); 224 | } 225 | 226 | //keep the loader in the loaded object in case caller wants to reuse the loader 227 | if (obj.gir != undefined) 228 | console.warn("[node-gir] Object provides it's own gir. Not replacing gir. Strange error? :-("); 229 | else 230 | obj.gir = this; 231 | 232 | //return the brutally overridden object 233 | return obj; 234 | }; 235 | 236 | /* END LOGIC */ 237 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gir", 3 | "description": "GObject Introspection Repository", 4 | "version": "0.1.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/creationix/node-gir.git" 8 | }, 9 | "author": "Tim Caswell ", 10 | "main": "gir.js", 11 | "directories": { 12 | "lib": "." 13 | }, 14 | "engines": { 15 | "node": "*" 16 | }, 17 | "devDependencies": { 18 | "should": "1.0.x", 19 | "mocha": "1.3.x" 20 | }, 21 | "scripts": { 22 | "test": "./node_modules/.bin/mocha tests/test_*.js tests/midgard/test_*.js" 23 | }, 24 | "dependencies": { 25 | "nan": "^2.0.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/arguments.cc: -------------------------------------------------------------------------------- 1 | #include "arguments.h" 2 | #include "util.h" 3 | #include "values.h" 4 | 5 | #include "types/object.h" 6 | #include 7 | 8 | #include 9 | 10 | using namespace v8; 11 | 12 | namespace gir { 13 | 14 | bool Args::ToGType(Handle v, GIArgument *arg, GIArgInfo *info, GITypeInfo *type_info, bool out) { 15 | GITypeInfo *type = type_info; 16 | if (info != nullptr) 17 | type = g_arg_info_get_type(info); 18 | GITypeTag tag = ReplaceGType(g_type_info_get_tag(type)); 19 | 20 | // nullify string so it be freed safely later 21 | arg->v_string = nullptr; 22 | 23 | if (out == TRUE) 24 | return true; 25 | 26 | if( ( v == Nan::Null() || v == Nan::Undefined() ) 27 | && (g_arg_info_may_be_null(info) 28 | || tag == GI_TYPE_TAG_VOID)) { 29 | arg->v_pointer = nullptr; 30 | return true; 31 | } 32 | if(tag == GI_TYPE_TAG_BOOLEAN) { 33 | arg->v_boolean = v->ToBoolean()->Value(); 34 | return true; 35 | } 36 | if(tag == GI_TYPE_TAG_INT8) { 37 | arg->v_int8 = v->ToNumber()->NumberValue(); 38 | return true; 39 | } 40 | if(tag == GI_TYPE_TAG_UINT8) { 41 | arg->v_uint8 = v->ToNumber()->NumberValue(); 42 | return true; 43 | } 44 | if(tag == GI_TYPE_TAG_INT16) { 45 | arg->v_int16 = v->ToNumber()->NumberValue(); 46 | return true; 47 | } 48 | if(tag == GI_TYPE_TAG_UINT16) { 49 | arg->v_uint16 = v->ToNumber()->NumberValue(); 50 | return true; 51 | } 52 | if(tag == GI_TYPE_TAG_INT32) { 53 | arg->v_int32 = v->ToInt32()->Value(); 54 | return true; 55 | } 56 | if(tag == GI_TYPE_TAG_UINT32) { 57 | arg->v_uint32 = v->ToUint32()->Value(); 58 | return true; 59 | } 60 | if(tag == GI_TYPE_TAG_INT64) { 61 | arg->v_int64 = v->ToInteger()->Value(); 62 | return true; 63 | } 64 | if(tag == GI_TYPE_TAG_UINT64) { 65 | arg->v_uint64 = v->ToInteger()->Value(); 66 | return true; 67 | } 68 | if(tag == GI_TYPE_TAG_FLOAT) { 69 | arg->v_float = v->ToNumber()->Value(); 70 | return true; 71 | } 72 | if(tag == GI_TYPE_TAG_DOUBLE) { 73 | arg->v_double = v->ToNumber()->Value(); 74 | return true; 75 | } 76 | if(tag == GI_TYPE_TAG_UTF8 || tag == GI_TYPE_TAG_FILENAME) { 77 | String::Utf8Value v8str(v->ToString()); 78 | arg->v_string = g_strdup(*v8str); 79 | return true; 80 | } 81 | if(tag == GI_TYPE_TAG_GLIST) { 82 | if(!v->IsArray()) { return false; } 83 | //GList *list = nullptr; 84 | //ArrayToGList(v, info, &list); // FIXME!!! 85 | return false; 86 | } 87 | if(tag == GI_TYPE_TAG_GSLIST) { 88 | if(!v->IsArray()) { return false; } 89 | //GSList *list = nullptr; 90 | //ArrayToGList(v, info, &list); // FIXME!!! 91 | return false; 92 | } 93 | if(tag == GI_TYPE_TAG_ARRAY) { 94 | if(!v->IsArray()) { 95 | if (v->IsString()) { 96 | String::Utf8Value _str(v->ToString()); 97 | arg->v_pointer = (gpointer *) g_strdup(*_str); 98 | return true; 99 | } 100 | return false; 101 | } 102 | 103 | GIArrayType arr_type = g_type_info_get_array_type(info); 104 | 105 | if(arr_type == GI_ARRAY_TYPE_C) { 106 | 107 | } 108 | else if(arr_type == GI_ARRAY_TYPE_ARRAY) { 109 | 110 | } 111 | else if(arr_type == GI_ARRAY_TYPE_PTR_ARRAY) { 112 | 113 | } 114 | else if(arr_type == GI_ARRAY_TYPE_BYTE_ARRAY) { 115 | 116 | } 117 | /* 118 | int l = g_type_info_get_array_length(info); 119 | for(int i=0; iIsObject()) { return false; } 126 | 127 | GITypeInfo *key_param_info, *val_param_info; 128 | //GHashTable *ghash; 129 | 130 | key_param_info = g_type_info_get_param_type(info, 0); 131 | g_assert(key_param_info != nullptr); 132 | val_param_info = g_type_info_get_param_type(info, 1); 133 | g_assert(val_param_info != nullptr); 134 | 135 | // TODO: implement 136 | 137 | g_base_info_unref((GIBaseInfo*) key_param_info); 138 | g_base_info_unref((GIBaseInfo*) val_param_info); 139 | 140 | return false; 141 | } 142 | if(tag == GI_TYPE_TAG_INTERFACE) { 143 | GIBaseInfo *interface_info = g_type_info_get_interface(type); 144 | g_assert(interface_info != nullptr); 145 | GIInfoType interface_type = g_base_info_get_type(interface_info); 146 | 147 | GType gtype; 148 | switch(interface_type) { 149 | case GI_INFO_TYPE_STRUCT: 150 | case GI_INFO_TYPE_ENUM: 151 | case GI_INFO_TYPE_OBJECT: 152 | case GI_INFO_TYPE_INTERFACE: 153 | case GI_INFO_TYPE_UNION: 154 | case GI_INFO_TYPE_BOXED: 155 | gtype = g_registered_type_info_get_g_type 156 | ((GIRegisteredTypeInfo*)interface_info); 157 | break; 158 | case GI_INFO_TYPE_VALUE: 159 | gtype = G_TYPE_VALUE; 160 | break; 161 | 162 | default: 163 | gtype = G_TYPE_NONE; 164 | break; 165 | } 166 | 167 | if(g_type_is_a(gtype, G_TYPE_OBJECT)) { 168 | if(!v->IsObject()) { return false; } 169 | GIRObject *gir_object = Nan::ObjectWrap::Unwrap(v->ToObject()); 170 | arg->v_pointer = gir_object->obj; 171 | return true; 172 | } 173 | if(g_type_is_a(gtype, G_TYPE_VALUE)) { 174 | GValue gvalue = {0, {{0}}}; 175 | if(!GIRValue::ToGValue(v, G_TYPE_INVALID, &gvalue)) { 176 | return false; 177 | } 178 | //FIXME I've to free this somewhere 179 | arg->v_pointer = g_boxed_copy(G_TYPE_VALUE, &gvalue); 180 | g_value_unset(&gvalue); 181 | return true; 182 | } 183 | } 184 | 185 | return false; 186 | } 187 | 188 | Handle Args::FromGTypeArray(GIArgument *arg, GITypeInfo *type, int array_length) { 189 | 190 | GITypeInfo *param_info = g_type_info_get_param_type(type, 0); 191 | //bool is_zero_terminated = g_type_info_is_zero_terminated(param_info); 192 | GITypeTag param_tag = g_type_info_get_tag(param_info); 193 | 194 | //g_base_info_unref(param_info); 195 | 196 | int i = 0; 197 | v8::Local arr; 198 | GIBaseInfo *interface_info = nullptr; 199 | 200 | switch(param_tag) { 201 | case GI_TYPE_TAG_UINT8: 202 | if (arg->v_pointer == nullptr) 203 | return Nan::New("", 0).ToLocalChecked(); 204 | // TODO, copy bytes to array 205 | // http://groups.google.com/group/v8-users/browse_thread/thread/8c5177923675749e?pli=1 206 | return Nan::New((char *)arg->v_pointer, array_length).ToLocalChecked(); 207 | 208 | case GI_TYPE_TAG_GTYPE: 209 | if (arg->v_pointer == nullptr) 210 | return Nan::New(); 211 | arr = Nan::New(array_length); 212 | for (i = 0; i < array_length; i++) { 213 | Nan::Set(arr, i, Nan::New((int)GPOINTER_TO_INT( ((gpointer*)arg->v_pointer)[i] ))); 214 | } 215 | return arr; 216 | 217 | case GI_TYPE_TAG_INTERFACE: 218 | if (arg->v_pointer == nullptr) 219 | return Nan::New(); 220 | arr = Nan::New(array_length); 221 | interface_info = g_type_info_get_interface(param_info); 222 | 223 | for (i = 0; i < array_length; i++) { 224 | GObject *o = (GObject*)((gpointer*)arg->v_pointer)[i]; 225 | arr->Set(i, GIRObject::New(o, interface_info)); 226 | } 227 | //g_base_info_unref(interface_info); // FIXME 228 | return arr; 229 | 230 | default: 231 | gchar *exc_msg = g_strdup_printf("Converting array of '%s' is not supported", g_type_tag_to_string(param_tag)); 232 | Nan::ThrowError(exc_msg); 233 | return Nan::Undefined(); 234 | } 235 | } 236 | 237 | Local Args::FromGType(GIArgument *arg, GITypeInfo *type, int array_length) { 238 | GITypeTag tag = g_type_info_get_tag(type); 239 | 240 | if(tag == GI_TYPE_TAG_INTERFACE) { 241 | GIBaseInfo *interface_info = g_type_info_get_interface(type); 242 | g_assert(interface_info != nullptr); 243 | GIInfoType interface_type = g_base_info_get_type(interface_info); 244 | 245 | if(interface_type == GI_INFO_TYPE_OBJECT) { 246 | return GIRObject::New(G_OBJECT(arg->v_pointer), interface_info); 247 | } 248 | } 249 | 250 | if(tag == GI_TYPE_TAG_INTERFACE) { 251 | GIBaseInfo *interface_info = g_type_info_get_interface(type); 252 | g_assert(interface_info != nullptr); 253 | GIInfoType interface_type = g_base_info_get_type(interface_info); 254 | 255 | GType gtype; 256 | switch(interface_type) { 257 | case GI_INFO_TYPE_STRUCT: 258 | case GI_INFO_TYPE_ENUM: 259 | case GI_INFO_TYPE_OBJECT: 260 | case GI_INFO_TYPE_INTERFACE: 261 | case GI_INFO_TYPE_UNION: 262 | case GI_INFO_TYPE_BOXED: 263 | gtype = g_registered_type_info_get_g_type 264 | ((GIRegisteredTypeInfo*)interface_info); 265 | break; 266 | case GI_INFO_TYPE_VALUE: 267 | gtype = G_TYPE_VALUE; 268 | break; 269 | 270 | default: 271 | gtype = G_TYPE_NONE; 272 | break; 273 | } 274 | 275 | if(g_type_is_a(gtype, G_TYPE_OBJECT)) { 276 | GObject *o = G_OBJECT(arg->v_pointer); 277 | return GIRObject::New(o, G_OBJECT_TYPE(o)); 278 | } 279 | if(g_type_is_a(gtype, G_TYPE_VALUE)) { 280 | GIRValue::FromGValue((GValue*)arg->v_pointer, nullptr); 281 | } 282 | } 283 | 284 | switch(tag) { 285 | case GI_TYPE_TAG_VOID: 286 | return Nan::Undefined(); 287 | case GI_TYPE_TAG_BOOLEAN: 288 | return Nan::New(arg->v_boolean); 289 | case GI_TYPE_TAG_INT8: 290 | return Nan::New(arg->v_int8); 291 | case GI_TYPE_TAG_UINT8: 292 | return Integer::NewFromUnsigned(v8::Isolate::GetCurrent(), arg->v_uint8); 293 | case GI_TYPE_TAG_INT16: 294 | return Nan::New(arg->v_int16); 295 | case GI_TYPE_TAG_UINT16: 296 | return Integer::NewFromUnsigned(v8::Isolate::GetCurrent(), arg->v_uint16); 297 | case GI_TYPE_TAG_INT32: 298 | return Nan::New(arg->v_int32); 299 | case GI_TYPE_TAG_UINT32: 300 | return Integer::NewFromUnsigned(v8::Isolate::GetCurrent(), arg->v_uint32); 301 | case GI_TYPE_TAG_INT64: 302 | return Nan::New((double) arg->v_int64); 303 | case GI_TYPE_TAG_UINT64: 304 | return Integer::NewFromUnsigned(v8::Isolate::GetCurrent(), arg->v_uint64); 305 | case GI_TYPE_TAG_FLOAT: 306 | return Nan::New(arg->v_float); 307 | case GI_TYPE_TAG_DOUBLE: 308 | return Nan::New(arg->v_double); 309 | case GI_TYPE_TAG_GTYPE: 310 | return Integer::NewFromUnsigned(v8::Isolate::GetCurrent(), arg->v_uint); 311 | case GI_TYPE_TAG_UTF8: 312 | return Nan::New(arg->v_string).ToLocalChecked(); 313 | case GI_TYPE_TAG_FILENAME: 314 | return Nan::New(arg->v_string).ToLocalChecked(); 315 | case GI_TYPE_TAG_ARRAY: 316 | return Args::FromGTypeArray(arg, type, array_length); 317 | case GI_TYPE_TAG_INTERFACE: 318 | return Nan::Undefined(); 319 | case GI_TYPE_TAG_GLIST: 320 | return Nan::Undefined(); 321 | case GI_TYPE_TAG_GSLIST: 322 | return Nan::Undefined(); 323 | case GI_TYPE_TAG_GHASH: 324 | return Nan::Undefined(); 325 | case GI_TYPE_TAG_ERROR: 326 | return Nan::Undefined(); 327 | case GI_TYPE_TAG_UNICHAR: 328 | return Nan::Undefined(); 329 | default: 330 | return Nan::Undefined(); 331 | } 332 | } 333 | 334 | GITypeTag Args::ReplaceGType(GITypeTag type) { 335 | if(type == GI_TYPE_TAG_GTYPE) { 336 | switch (sizeof(GType)) { 337 | case 1: return GI_TYPE_TAG_UINT8; 338 | case 2: return GI_TYPE_TAG_UINT16; 339 | case 4: return GI_TYPE_TAG_UINT32; 340 | case 8: return GI_TYPE_TAG_UINT64; 341 | default: g_assert_not_reached(); 342 | } 343 | } 344 | return type; 345 | } 346 | 347 | bool Args::ArrayToGList(Handle arr, GIArgInfo *info, GList **list_p) { 348 | GList *list = nullptr; 349 | 350 | int l = arr->Length(); 351 | for(int i=0; iGet(Nan::New(i)), &arg, g_type_info_get_param_type(info, 0), nullptr, FALSE)) { 354 | return false; 355 | } 356 | list = g_list_prepend(list, arg.v_pointer); 357 | } 358 | 359 | list = g_list_reverse(list); 360 | *list_p = list; 361 | 362 | return true; 363 | } 364 | 365 | bool Args::ArrayToGList(Handle arr, GIArgInfo *info, GSList **slist_p) { 366 | GSList *slist = nullptr; 367 | 368 | int l = arr->Length(); 369 | for(int i=0; iGet(Nan::New(i)), &arg, g_type_info_get_param_type(info, 0), nullptr, FALSE)) { 372 | return false; 373 | } 374 | slist = g_slist_prepend(slist, arg.v_pointer); 375 | } 376 | 377 | slist = g_slist_reverse(slist); 378 | *slist_p = slist; 379 | 380 | return true; 381 | } 382 | 383 | 384 | } 385 | -------------------------------------------------------------------------------- /src/arguments.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace gir { 8 | 9 | class Args { 10 | public: 11 | static bool ToGType(v8::Handle, GIArgument *arg, GIArgInfo *info, GITypeInfo *type_info, bool out); 12 | static v8::Handle FromGTypeArray(GIArgument *arg, GIArgInfo *info, int array_length); 13 | static v8::Local FromGType(GIArgument *arg, GIArgInfo *info, int array_length); 14 | static inline GITypeTag ReplaceGType(GITypeTag type); 15 | static bool ArrayToGList(v8::Handle arr, GIArgInfo *info, GList **list_p); 16 | static bool ArrayToGList(v8::Handle arr, GIArgInfo *info, GSList **list_p); 17 | }; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/function.cc: -------------------------------------------------------------------------------- 1 | #include "function.h" 2 | #include "arguments.h" 3 | #include "util.h" 4 | #include 5 | #include 6 | 7 | using namespace v8; 8 | 9 | namespace gir { 10 | 11 | static GIArgument *_gir_gi_argument_new(GObject *obj, int length) 12 | { 13 | if (length < 1) 14 | return nullptr; 15 | 16 | GIArgument *args = g_new0(GIArgument, length); 17 | for (int i = 0; i < length; i++) { 18 | args[i].v_string = nullptr; 19 | } 20 | 21 | if (obj != nullptr) 22 | args[0].v_pointer = obj; 23 | 24 | return args; 25 | } 26 | 27 | static void _gir_gi_argument_free(GIArgument *args, int length) 28 | { 29 | if (length < 1) 30 | return; 31 | 32 | for (int i = 0; i < length; i++) { 33 | //g_free(args[i].v_string); FIXME, it has to be freed 34 | } 35 | g_free(args); 36 | args = nullptr; 37 | } 38 | 39 | /* Check that the function is called with the correct number of arguments. 40 | * Returns a newly allocated exception message string on failure, or NULL on success. */ 41 | char * checkNumberOfArguments(GIFunctionInfo *info, const Nan::FunctionCallbackInfo& args, 42 | int *in_arguments_count, int *out_arguments_count) { 43 | 44 | int in_argc = *in_arguments_count; 45 | int out_argc = *out_arguments_count; 46 | 47 | const int l = g_callable_info_get_n_args(info); 48 | int required_arguments = l; 49 | int optional_arguments = 0; 50 | 51 | // Compute number of required arguments 52 | // Any out and error argument should be implicit 53 | for (int i=0; i Func::CallAndGetPtr(GObject *obj, GIFunctionInfo *info, const Nan::FunctionCallbackInfo &args, bool ignore_function_name, GIArgument *retval, GITypeInfo **returned_type_info, gint *returned_array_length) { 106 | if(g_function_info_get_flags(info) == GI_FUNCTION_IS_CONSTRUCTOR) { 107 | // rly not sure about this 108 | debug_printf("constructor! returns %s\n", g_type_tag_to_string( g_type_info_get_tag( g_callable_info_get_return_type(info) ) )); 109 | obj = nullptr; 110 | } 111 | 112 | *returned_array_length = -1; 113 | *returned_type_info = g_callable_info_get_return_type(info); 114 | const int offset_ = (obj != nullptr) ? 1 : 0; 115 | const int l = g_callable_info_get_n_args(info); 116 | int in_argc_c_length = offset_, out_argc_c_length = 0; 117 | 118 | // Verify that function is called with right number of arguments 119 | char *exc_msg = checkNumberOfArguments(info, args, &in_argc_c_length, &out_argc_c_length); 120 | if (exc_msg) { 121 | Nan::ThrowTypeError(exc_msg); 122 | return Nan::Null(); 123 | } 124 | debug_printf("(%d) in_argc_c_length is %d, out_argc_c_length is %d, offset is %d\n", l, in_argc_c_length, out_argc_c_length, offset_); 125 | 126 | GIArgument *in_args = _gir_gi_argument_new(obj, in_argc_c_length); 127 | GIArgument *out_args = _gir_gi_argument_new(nullptr, out_argc_c_length); 128 | gpointer *out_args_c = nullptr; 129 | if (out_argc_c_length > 0) { 130 | out_args_c = g_new0(gpointer, out_argc_c_length); 131 | } 132 | 133 | const int returned_array_real_pos = g_type_info_get_array_length(*returned_type_info); 134 | int returned_array_pos = -1; 135 | int in_c = offset_, out_c = 0; 136 | for(int i=0; imessage); 196 | } 197 | 198 | // TODO, set out values 199 | 200 | GITypeTag tag = g_type_info_get_tag(*returned_type_info); 201 | // Set returned array length 202 | if (tag == GI_TYPE_TAG_ARRAY) { 203 | if (returned_array_pos > -1) { 204 | *returned_array_length = (int) GPOINTER_TO_INT(out_args_c[returned_array_pos]); 205 | } 206 | } 207 | 208 | _gir_gi_argument_free(in_args, in_argc_c_length); 209 | _gir_gi_argument_free(out_args, out_argc_c_length); 210 | g_free(out_args_c); 211 | 212 | return Nan::Null(); 213 | } 214 | 215 | Local Func::Call(GObject *obj, GIFunctionInfo *info, const Nan::FunctionCallbackInfo&args, bool ignore_function_name) { 216 | 217 | if(g_function_info_get_flags(info) == GI_FUNCTION_IS_CONSTRUCTOR) { 218 | // rly not sure about this 219 | debug_printf("constructor! returns %s\n", g_type_tag_to_string( g_type_info_get_tag( g_callable_info_get_return_type(info) ) )); 220 | obj = nullptr; 221 | } 222 | 223 | GIArgument retval; 224 | GITypeInfo *returned_type_info; 225 | gint returned_array_length; 226 | 227 | CallAndGetPtr(obj, info, args, ignore_function_name, &retval, &returned_type_info, &returned_array_length); 228 | Local return_value = Args::FromGType(&retval, returned_type_info, returned_array_length); 229 | 230 | if (returned_type_info != nullptr) 231 | g_base_info_unref(returned_type_info); 232 | 233 | /* TODO, free GIArgument ? */ 234 | 235 | return return_value; 236 | } 237 | 238 | NAN_METHOD(Func::CallStaticMethod) 239 | { 240 | v8::Handle info_ptr = v8::Handle::Cast(info.Callee()->GetHiddenValue(Nan::New("GIInfo").ToLocalChecked())); 241 | GIBaseInfo *func = (GIBaseInfo*) info_ptr->Value(); 242 | GIBaseInfo *container = g_base_info_get_container(func); 243 | 244 | debug_printf("Call static method '%s.%s.%s' ('%s') \n", 245 | g_base_info_get_namespace(func), 246 | g_base_info_get_name(container), 247 | g_base_info_get_name(func), 248 | g_function_info_get_symbol(func)); 249 | 250 | if (func) { 251 | info.GetReturnValue().Set(Func::Call(nullptr, func, info, TRUE)); 252 | } 253 | else { 254 | Nan::ThrowError("no such method"); 255 | } 256 | 257 | info.GetReturnValue().Set(Nan::Undefined()); 258 | } 259 | 260 | } 261 | -------------------------------------------------------------------------------- /src/function.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace gir { 9 | 10 | class Func { 11 | public: 12 | static v8::Local Call(GObject *obj, GIFunctionInfo *info, const Nan::FunctionCallbackInfo&args, bool ignore_function_name); 13 | static v8::Handle CallAndGetPtr(GObject *obj, GIFunctionInfo *info, const Nan::FunctionCallbackInfo&args, bool ignore_function_name, GIArgument *retval, GITypeInfo **returned_type_info, gint *returned_array_length); 14 | static NAN_METHOD(CallStaticMethod); 15 | }; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/init.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "util.h" 6 | #include "namespace_loader.h" 7 | 8 | NAN_MODULE_INIT(InitAll) { 9 | Nan::Set(target, Nan::New("load").ToLocalChecked(), 10 | Nan::GetFunction(Nan::New(gir::NamespaceLoader::Load)).ToLocalChecked()); 11 | Nan::Set(target, Nan::New("search_path").ToLocalChecked(), 12 | Nan::GetFunction(Nan::New(gir::NamespaceLoader::SearchPath)).ToLocalChecked()); 13 | } 14 | 15 | NODE_MODULE(girepository, InitAll) 16 | -------------------------------------------------------------------------------- /src/namespace_loader.cc: -------------------------------------------------------------------------------- 1 | #include "namespace_loader.h" 2 | #include "util.h" 3 | 4 | #include "types/object.h" 5 | #include "types/function_type.h" 6 | #include "types/struct.h" 7 | 8 | #include 9 | 10 | using namespace v8; 11 | 12 | namespace gir { 13 | 14 | GIRepository *NamespaceLoader::repo = nullptr; 15 | std::map NamespaceLoader::type_libs; 16 | 17 | NAN_METHOD(NamespaceLoader::Load) { 18 | if (info.Length() < 1) { 19 | Nan::ThrowError("too few arguments"); 20 | } 21 | if (!info[0]->IsString()) { 22 | Nan::ThrowError("argument has to be a string"); 23 | } 24 | 25 | Local exports; 26 | String::Utf8Value namespace_(info[0]); 27 | 28 | if(info.Length() > 1 && info[1]->IsString()) { 29 | String::Utf8Value version(info[1]); 30 | exports = NamespaceLoader::LoadNamespace(*namespace_, *version); 31 | } 32 | else { 33 | exports = NamespaceLoader::LoadNamespace(*namespace_, nullptr); 34 | } 35 | 36 | info.GetReturnValue().Set(exports); 37 | } 38 | 39 | Handle NamespaceLoader::LoadNamespace(char *namespace_, char *version) { 40 | if (!repo) { 41 | repo = g_irepository_get_default(); 42 | } 43 | 44 | GError *er = NULL; 45 | GITypelib *lib = g_irepository_require(repo, namespace_, version, (GIRepositoryLoadFlags)0, &er); 46 | if (!lib) { 47 | Nan::ThrowError(er->message); 48 | } 49 | 50 | type_libs.insert(std::make_pair(namespace_, lib)); 51 | 52 | Handle res = BuildClasses(namespace_); 53 | return res; 54 | } 55 | 56 | Handle NamespaceLoader::BuildClasses(char *namespace_) { 57 | Handle exports = Nan::New(); 58 | 59 | int length = g_irepository_get_n_infos(repo, namespace_); 60 | for (int i = 0; i < length; i++) { 61 | GIBaseInfo *info = g_irepository_get_info(repo, namespace_, i); 62 | 63 | switch(g_base_info_get_type(info)) { 64 | case GI_INFO_TYPE_BOXED: 65 | //FIXME: GIStructInfo or GIUnionInfo 66 | case GI_INFO_TYPE_STRUCT: 67 | GIRStruct::Prepare(exports, (GIStructInfo*)info); 68 | break; 69 | case GI_INFO_TYPE_ENUM: 70 | ParseEnum((GIEnumInfo*)info, exports); 71 | break; 72 | case GI_INFO_TYPE_FLAGS: 73 | ParseFlags((GIEnumInfo*)info, exports); 74 | break; 75 | case GI_INFO_TYPE_OBJECT: 76 | GIRObject::Prepare(exports, (GIObjectInfo*)info); 77 | break; 78 | case GI_INFO_TYPE_INTERFACE: 79 | ParseInterface((GIInterfaceInfo*)info, exports); 80 | break; 81 | case GI_INFO_TYPE_UNION: 82 | ParseUnion((GIUnionInfo*)info, exports); 83 | break; 84 | case GI_INFO_TYPE_FUNCTION: 85 | GIRFunction::Initialize(exports, (GIFunctionInfo*)info); 86 | break; 87 | case GI_INFO_TYPE_INVALID: 88 | case GI_INFO_TYPE_CALLBACK: 89 | case GI_INFO_TYPE_CONSTANT: 90 | case GI_INFO_TYPE_INVALID_0: 91 | case GI_INFO_TYPE_VALUE: 92 | case GI_INFO_TYPE_SIGNAL: 93 | case GI_INFO_TYPE_VFUNC: 94 | case GI_INFO_TYPE_PROPERTY: 95 | case GI_INFO_TYPE_FIELD: 96 | case GI_INFO_TYPE_ARG: 97 | case GI_INFO_TYPE_TYPE: 98 | case GI_INFO_TYPE_UNRESOLVED: 99 | // Do nothing 100 | break; 101 | } 102 | 103 | g_base_info_unref(info); 104 | } 105 | 106 | // when all classes have been created we can inherit them 107 | GIRObject::Initialize(exports, namespace_); 108 | GIRStruct::Initialize(exports, namespace_); 109 | 110 | return exports; 111 | } 112 | 113 | void NamespaceLoader::ParseStruct(GIStructInfo *info, Handle &exports) { 114 | 115 | } 116 | 117 | void NamespaceLoader::ParseEnum(GIEnumInfo *info, Handle &exports) { 118 | Handle obj = Nan::New(); 119 | 120 | int length = g_enum_info_get_n_values(info); 121 | for(int i=0; i(g_value_info_get_value(value))); 124 | g_base_info_unref(value); 125 | } 126 | Nan::Set(exports, Nan::New(g_base_info_get_name(info)).ToLocalChecked(), obj); 127 | } 128 | 129 | void NamespaceLoader::ParseFlags(GIEnumInfo *info, Handle &exports) { 130 | Handle obj = Nan::New(); 131 | 132 | int length = g_enum_info_get_n_values(info); 133 | for(int i=0; i &exports) { 141 | 142 | } 143 | 144 | void NamespaceLoader::ParseUnion(GIUnionInfo *info, Handle &exports) { 145 | 146 | } 147 | 148 | NAN_METHOD(NamespaceLoader::SearchPath) { 149 | if(!repo) { 150 | repo = g_irepository_get_default(); 151 | } 152 | GSList *ls = g_irepository_get_search_path(); 153 | int l = g_slist_length(ls); 154 | Local res = Nan::New(l); 155 | 156 | for(int i=0; i 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace gir { 10 | 11 | class NamespaceLoader : public Nan::ObjectWrap { 12 | public: 13 | static GIRepository *repo; 14 | static std::map type_libs; 15 | 16 | static NAN_METHOD(Load); 17 | static NAN_METHOD(SearchPath); 18 | 19 | private: 20 | static v8::Handle LoadNamespace(char *namespace_, char *version); 21 | static v8::Handle BuildClasses(char *namespace_); 22 | 23 | static void ParseStruct(GIStructInfo *info, v8::Handle &exports); 24 | static void ParseEnum(GIEnumInfo *info, v8::Handle &exports); 25 | static void ParseFlags(GIEnumInfo *info, v8::Handle &exports); 26 | static void ParseInterface(GIInterfaceInfo *info, v8::Handle &exports); 27 | static void ParseUnion(GIUnionInfo *info, v8::Handle &exports); 28 | static void ParseFunction(GIFunctionInfo *info, v8::Handle &exports); 29 | 30 | }; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/types/function_type.cc: -------------------------------------------------------------------------------- 1 | #include "function_type.h" 2 | #include "../namespace_loader.h" 3 | #include "../util.h" 4 | #include "../function.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace v8; 11 | 12 | namespace gir { 13 | 14 | void GIRFunction::Initialize(Handle target, GIObjectInfo *info) 15 | { 16 | const char *func_name = g_base_info_get_name(info); 17 | // Create new function 18 | Local temp = Nan::New(Execute); 19 | // Set name 20 | temp->GetFunction()->SetName(Nan::New(func_name).ToLocalChecked()); 21 | // Create external to hold GIBaseInfo and set it 22 | v8::Handle info_ptr = Nan::New((void*)g_base_info_ref(info)); 23 | temp->GetFunction()->SetHiddenValue(Nan::New("GIInfo").ToLocalChecked(), info_ptr); 24 | // Set symbol 25 | target->Set(Nan::New(func_name).ToLocalChecked(), temp->GetFunction()); 26 | } 27 | 28 | NAN_METHOD(GIRFunction::Execute) 29 | { 30 | // Get GIFunctionInfo pointer 31 | v8::Handle info_ptr = 32 | v8::Handle::Cast(info.Callee()->GetHiddenValue(Nan::New("GIInfo").ToLocalChecked())); 33 | GIBaseInfo *func = (GIBaseInfo*) info_ptr->Value(); 34 | 35 | debug_printf("EXECUTE namespace: '%s', name: '%s', symbol: '%s' \n", 36 | g_base_info_get_namespace(func), 37 | g_base_info_get_name(func), 38 | g_function_info_get_symbol(func)); 39 | 40 | if(func) { 41 | info.GetReturnValue().Set(Func::Call(NULL, func, info, TRUE)); 42 | } 43 | else { 44 | Nan::ThrowError("no such function"); 45 | } 46 | 47 | info.GetReturnValue().SetUndefined(); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/types/function_type.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace gir { 10 | 11 | class GIRFunction : public Nan::ObjectWrap { 12 | public: 13 | GIRFunction() {}; 14 | 15 | static void Initialize(v8::Handle target, GIObjectInfo *info); 16 | static NAN_METHOD(Execute); 17 | static char* ToCamelCase(const char *str); 18 | 19 | private: 20 | }; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/types/object.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "object.h" 4 | #include "function_type.h" 5 | #include "../util.h" 6 | #include "../function.h" 7 | #include "../values.h" 8 | #include "../namespace_loader.h" 9 | 10 | #include 11 | #include 12 | 13 | using namespace v8; 14 | using namespace std; 15 | 16 | namespace gir { 17 | 18 | void empty_func(void) {}; 19 | 20 | std::vector GIRObject::templates; 21 | std::vector GIRObject::instances; 22 | GIPropertyInfo *g_object_info_find_property(GIObjectInfo *info, char *name); 23 | 24 | GIRObject::GIRObject(GIObjectInfo *info_, int n_params, GParameter *parameters) 25 | { 26 | info = info_; 27 | GType t = g_registered_type_info_get_g_type(info); 28 | 29 | abstract = g_object_info_get_abstract(info); 30 | if (abstract) { 31 | obj = nullptr; 32 | } 33 | else { 34 | // gobject va_list, to allow construction parameters 35 | obj = G_OBJECT(g_object_newv(t, n_params, parameters)); 36 | debug_printf("New GObject [%p] '%s' \n", obj,G_OBJECT_TYPE_NAME(obj)); 37 | } 38 | } 39 | 40 | GIObjectInfo * 41 | _get_object_info(GType obj_type, GIObjectInfo *info) 42 | { 43 | // The main purpose of this function is to get the best matching info. 44 | // For example, we have class C which extends B and this extends A. 45 | // Function x() returns C instances, while it's introspected to return A instances. 46 | // If C info is not found, we try B as it's the first ancestor of C, etc. 47 | GIBaseInfo *tmp_info = g_irepository_find_by_gtype(NamespaceLoader::repo, obj_type); 48 | if (tmp_info != nullptr && g_base_info_equal(tmp_info, info)) 49 | return g_base_info_ref(tmp_info); 50 | GType parent_type = g_type_parent(obj_type); 51 | if (tmp_info == nullptr) 52 | return g_irepository_find_by_gtype(NamespaceLoader::repo, parent_type); 53 | return tmp_info; 54 | } 55 | 56 | Handle GIRObject::New(GObject *obj_, GIObjectInfo *info_) 57 | { 58 | // find the function template 59 | if (obj_ == nullptr || !G_IS_OBJECT(obj_)) { 60 | return Nan::Null(); 61 | } 62 | 63 | Handle res = GetInstance(obj_); 64 | if (res != Nan::Null()) { 65 | return res; 66 | } 67 | Handle arg = Nan::New(false); 68 | std::vector::iterator it; 69 | 70 | GIObjectInfo *object_info = _get_object_info(G_OBJECT_TYPE(obj_), info_); 71 | if (!object_info) { 72 | gchar *msg = g_strdup_printf("ObjectInfo not found for '%s'", G_OBJECT_TYPE_NAME(obj_)); 73 | Nan::ThrowTypeError(msg); 74 | } 75 | for (it = templates.begin(); it != templates.end(); ++it) { 76 | if (g_base_info_equal(object_info, it->info)) { 77 | res = it->function->GetFunction()->NewInstance(1, &arg); 78 | if (!res.IsEmpty()) { 79 | GIRObject *e = ObjectWrap::Unwrap(res->ToObject()); 80 | e->info = object_info; 81 | e->obj = obj_; 82 | e->abstract = false; 83 | g_base_info_unref(object_info); 84 | return res; 85 | } 86 | break; 87 | } 88 | } 89 | if (object_info) 90 | g_base_info_unref(object_info); 91 | 92 | return Nan::Null(); 93 | } 94 | 95 | Handle GIRObject::New(GObject *obj_, GType t) 96 | { 97 | if (obj_ == nullptr || !G_IS_OBJECT(obj_)) { 98 | return Nan::Null(); 99 | } 100 | 101 | Handle res = GetInstance(obj_); 102 | if (res != Nan::Null()) { 103 | return res; 104 | } 105 | 106 | Handle arg = Nan::New(false); 107 | std::vector::iterator it; 108 | GIBaseInfo *base_info = g_irepository_find_by_gtype(NamespaceLoader::repo, t); 109 | if (base_info == nullptr) { 110 | base_info = g_irepository_find_by_gtype(NamespaceLoader::repo, g_type_parent(t)); 111 | } 112 | /*printf("CREATE NEW OBJECT WITH TYPE '%s' BASE '%s' \n", 113 | G_OBJECT_TYPE_NAME(t), 114 | g_base_info_get_name(base_info)); */ 115 | for (it = templates.begin(); it != templates.end(); ++it) { 116 | if (t == it->type) { 117 | res = it->function->GetFunction()->NewInstance(1, &arg); 118 | if (!res.IsEmpty()) { 119 | GIRObject *e = ObjectWrap::Unwrap(res->ToObject()); 120 | e->info = it->info; 121 | e->obj = obj_; 122 | e->abstract = false; 123 | return res; 124 | } 125 | return Nan::Null(); 126 | } 127 | } 128 | return Nan::Null(); 129 | } 130 | 131 | NAN_METHOD(GIRObject::New) 132 | { 133 | if (info.Length() == 1 && info[0]->IsBoolean() && !info[0]->IsTrue()) { 134 | GIRObject *obj = new GIRObject(); 135 | obj->Wrap(info.This()); 136 | PushInstance(obj, info.This()); 137 | info.GetReturnValue().Set(info.This()); 138 | } 139 | 140 | String::Utf8Value className(info.Callee()->GetName()); 141 | 142 | debug_printf ("CTR '%s' \n", *className); 143 | std::vector::iterator it; 144 | 145 | GIObjectInfo *objinfo = nullptr; 146 | for (it = templates.begin(); it != templates.end(); ++it) { 147 | if (strcmp(it->type_name, *className) == 0) { 148 | objinfo = it->info; 149 | break; 150 | } 151 | } 152 | 153 | if (objinfo == nullptr) { 154 | Nan::ThrowError("no such class. Callee()->GetName() returned wrong classname"); 155 | } 156 | 157 | int length = 0; 158 | GParameter *params = nullptr; 159 | Local v = ToParams(info[0], ¶ms, &length, objinfo); 160 | if (v != Nan::Null()) 161 | info.GetReturnValue().Set(v); 162 | 163 | GIRObject *obj = new GIRObject(objinfo, length, params); 164 | DeleteParams(params, length); 165 | 166 | obj->Wrap(info.This()); 167 | PushInstance(obj, info.This()); 168 | info.GetReturnValue().Set(info.This()); 169 | } 170 | 171 | GIRObject::~GIRObject() 172 | { 173 | // This destructor willbe called only (and only) if object is garabage collected 174 | // For persistant destructor see Node::AtExit 175 | // http://prox.moraphi.com/index.php/https/github.com/bnoordhuis/node/commit/1c20cac 176 | } 177 | 178 | Handle GIRObject::ToParams(Handle val, GParameter** params, int *length, GIObjectInfo *info) 179 | { 180 | *length = 0; 181 | *params = nullptr; 182 | if (!val->IsObject()) { 183 | return Nan::Null(); 184 | } 185 | Handle obj = val->ToObject(); 186 | 187 | Handle props = obj->GetPropertyNames(); 188 | *length = props->Length(); 189 | *params = g_new0(GParameter, *length); 190 | GParamSpec *pspec = nullptr; 191 | GObjectClass *klass = (GObjectClass*) g_type_class_ref(g_type_from_name(g_object_info_get_type_name(info))); 192 | for (int i=0; i<*length; i++) { 193 | String::Utf8Value key(props->Get(i)->ToString()); 194 | 195 | // nullify name so it can be freed safely 196 | (*params)[i].name = nullptr; 197 | 198 | if (!FindProperty(info, *key)) { 199 | /* Try to find property spec registered for given class */ 200 | if (klass) { 201 | pspec = g_object_class_find_property(klass, *key); 202 | } 203 | 204 | if (!pspec) { 205 | DeleteParams(*params, (*length)-1); 206 | gchar *msg = g_strdup_printf("Can not find '%s' property", *key); 207 | Nan::ThrowTypeError(msg); 208 | } 209 | } 210 | 211 | GValue gvalue = {0, {{0}}}; 212 | GType value_type = G_TYPE_INVALID; 213 | // Determine the best match for property's type 214 | if (klass) { 215 | if (!pspec) 216 | pspec = g_object_class_find_property(klass, *key); 217 | 218 | if (pspec) 219 | value_type = pspec->value_type; 220 | } 221 | if (!GIRValue::ToGValue(obj->Get(props->Get(i)), value_type, &gvalue)) { 222 | DeleteParams(*params, (*length)-1); 223 | gchar *msg = g_strdup_printf("'%s' property value conversion failed", *key); 224 | Nan::ThrowTypeError(msg); 225 | } 226 | 227 | (*params)[i].name = g_strdup(*key); 228 | (*params)[i].value = gvalue; 229 | 230 | pspec = nullptr; 231 | } 232 | 233 | if (klass) 234 | g_type_class_unref(klass); 235 | 236 | return Nan::Null(); 237 | } 238 | 239 | void GIRObject::DeleteParams(GParameter *params, int l) 240 | { 241 | if (params == nullptr) 242 | return; 243 | 244 | for (int i=0; i info_ptr = Handle::Cast(info.Data()); 257 | GIBaseInfo *base_info = (GIBaseInfo*) info_ptr->Value(); 258 | if (base_info != nullptr) { 259 | GIRObject *that = Nan::ObjectWrap::Unwrap(info.This()->ToObject()); 260 | GParamSpec *pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(that->obj), *_name); 261 | if (pspec) { 262 | // Property is not readable 263 | if (!(pspec->flags & G_PARAM_READABLE)) { 264 | Nan::ThrowTypeError("property is not readable"); 265 | } 266 | 267 | debug_printf("GetHandler (Get property) [%p] '%s.%s' \n", that->obj, G_OBJECT_TYPE_NAME(that->obj), *_name); 268 | 269 | GIPropertyInfo *prop_info = g_object_info_find_property(base_info, *_name); 270 | GType value_type = G_TYPE_FUNDAMENTAL(pspec->value_type); 271 | GValue gvalue = {0, {{0}}}; 272 | g_value_init(&gvalue, pspec->value_type); 273 | g_object_get_property(G_OBJECT(that->obj), *_name, &gvalue); 274 | Local res = GIRValue::FromGValue(&gvalue, prop_info); 275 | if (value_type != G_TYPE_OBJECT && value_type != G_TYPE_BOXED) { 276 | g_value_unset(&gvalue); 277 | } 278 | if (prop_info) 279 | g_base_info_unref(prop_info); 280 | 281 | info.GetReturnValue().Set(res); 282 | } 283 | } 284 | 285 | // Fallback to defaults 286 | info.GetReturnValue().Set(info.This()->GetPrototype()->ToObject()->Get(property)); 287 | } 288 | 289 | NAN_PROPERTY_QUERY(PropertyQueryHandler) 290 | { 291 | String::Utf8Value _name(property); 292 | debug_printf("QUERY HANDLER '%s' \n", *_name); 293 | info.GetReturnValue().Set(Nan::New(0)); 294 | } 295 | 296 | NAN_PROPERTY_SETTER(PropertySetHandler) 297 | { 298 | String::Utf8Value _name(property); 299 | 300 | v8::Handle info_ptr = v8::Handle::Cast(info.Data()); 301 | GIBaseInfo *base_info = (GIBaseInfo*) info_ptr->Value(); 302 | if (base_info != nullptr) { 303 | GIRObject *that = Nan::ObjectWrap::Unwrap(info.This()->ToObject()); 304 | GParamSpec *pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(that->obj), *_name); 305 | if (pspec) { 306 | // Property is not readable 307 | if (!(pspec->flags & G_PARAM_WRITABLE)) { 308 | Nan::ThrowTypeError("property is not writable"); 309 | } 310 | 311 | debug_printf("SetHandler (Set property) '%s.%s' \n", G_OBJECT_TYPE_NAME(that->obj), *_name); 312 | 313 | GValue gvalue = {0, {{0}}}; 314 | bool value_is_set = GIRValue::ToGValue(value, pspec->value_type, &gvalue); 315 | g_object_set_property(G_OBJECT(that->obj), *_name, &gvalue); 316 | GType value_type = G_TYPE_FUNDAMENTAL(pspec->value_type); 317 | if (value_type != G_TYPE_OBJECT && value_type != G_TYPE_BOXED) { 318 | g_value_unset(&gvalue); 319 | } 320 | info.GetReturnValue().Set(Nan::New(value_is_set)); 321 | } 322 | } 323 | 324 | // Fallback to defaults 325 | info.GetReturnValue().Set(Nan::New(info.This()->GetPrototype()->ToObject()->Set(property, value))); 326 | } 327 | 328 | void GIRObject::Prepare(Handle target, GIObjectInfo *info) 329 | { 330 | char *name = (char*)g_base_info_get_name(info); 331 | const char *namespace_ = g_base_info_get_namespace(info); 332 | g_base_info_ref(info); 333 | 334 | Local t = Nan::New(New); 335 | t->SetClassName(Nan::New(name).ToLocalChecked()); 336 | 337 | ObjectFunctionTemplate oft; 338 | oft.type_name = name; 339 | oft.info = info; 340 | oft.function = t; 341 | oft.type = g_registered_type_info_get_g_type(info); 342 | oft.namespace_ = (char*)namespace_; 343 | 344 | templates.push_back(oft); 345 | 346 | // Create instance template 347 | v8::Local instance_t = t->InstanceTemplate(); 348 | instance_t->SetInternalFieldCount(1); 349 | // Create external to hold GIBaseInfo and set it 350 | v8::Handle info_handle = Nan::New((void*)g_base_info_ref(info)); 351 | // Set properties handlers 352 | SetNamedPropertyHandler(instance_t, 353 | PropertyGetHandler, 354 | PropertySetHandler, 355 | PropertyQueryHandler, 356 | nullptr, 357 | nullptr, 358 | info_handle); 359 | 360 | t->Set(Nan::New("__properties__").ToLocalChecked(), PropertyList(info)); 361 | t->Set(Nan::New("__methods__").ToLocalChecked(), MethodList(info)); 362 | t->Set(Nan::New("__interfaces__").ToLocalChecked(), InterfaceList(info)); 363 | t->Set(Nan::New("__fields__").ToLocalChecked(), FieldList(info)); 364 | t->Set(Nan::New("__signals__").ToLocalChecked(), SignalList(info)); 365 | t->Set(Nan::New("__v_funcs__").ToLocalChecked(), VFuncList(info)); 366 | t->Set(Nan::New("__abstract__").ToLocalChecked(), Nan::New(g_object_info_get_abstract(info))); 367 | 368 | int l = g_object_info_get_n_constants(info); 369 | for (int i=0; iSet(Nan::New(g_base_info_get_name(constant)).ToLocalChecked(), Nan::New(i)); 372 | g_base_info_unref(constant); 373 | } 374 | 375 | RegisterMethods(target, info, namespace_, t); 376 | SetPrototypeMethods(t, name); 377 | } 378 | 379 | void GIRObject::Initialize(Handle target, char *namespace_) 380 | { 381 | // this gets called when all classes have been initialized 382 | std::vector::iterator it; 383 | std::vector::iterator temp; 384 | GIObjectInfo* parent; 385 | 386 | for (it = templates.begin(); it != templates.end(); ++it) { 387 | parent = g_object_info_get_parent(it->info); 388 | if (strcmp(it->namespace_, namespace_) != 0 || !parent) { 389 | continue; 390 | } 391 | 392 | for (temp = templates.begin(); temp != templates.end(); ++temp) { 393 | if (g_base_info_equal(temp->info, parent)) { 394 | it->function->Inherit(temp->function); 395 | } 396 | } 397 | } 398 | for (it = templates.begin(); it != templates.end(); ++it) { 399 | if (strcmp(it->namespace_, namespace_) == 0) { 400 | target->Set(Nan::New(g_base_info_get_name(it->info)).ToLocalChecked(), it->function->GetFunction()); 401 | } 402 | } 403 | } 404 | 405 | void GIRObject::SetPrototypeMethods(Local t, char *name) 406 | { 407 | Nan::SetPrototypeMethod(t, "__get_property__", GetProperty); 408 | Nan::SetPrototypeMethod(t, "__set_property__", SetProperty); 409 | Nan::SetPrototypeMethod(t, "__get_interface__", GetInterface); 410 | Nan::SetPrototypeMethod(t, "__get_field__", GetField); 411 | Nan::SetPrototypeMethod(t, "__watch_signal__", WatchSignal); 412 | Nan::SetPrototypeMethod(t, "__call_v_func__", CallMethod); 413 | } 414 | 415 | Handle GIRObject::Emit(Handle argv[], int length) 416 | { 417 | //String::Utf8Value cname(handle()->GetConstructorName()); 418 | //String::Utf8Value signal(argv[0]); 419 | 420 | //printf ("Emit, handle is '%s' '%s' [%p], length (%d) \n", *cname, *signal, handle(), length); 421 | 422 | // this will do the magic but dont forget to extend this object in JS from require("events").EventEmitter 423 | Local emit_v = handle()->Get(Nan::New("emit").ToLocalChecked()); 424 | //printf ("EMIT PTR IS [%p] \n", emit_v); 425 | //v8::String::AsciiValue ename(emit_v->ToString()); 426 | //printf ("Emit, emit is '%s' \n", *ename); 427 | if (emit_v->IsUndefined() || !emit_v->IsFunction()) { 428 | return Nan::Null(); 429 | } 430 | 431 | Local emit = Local::Cast(emit_v); 432 | return emit->Call(handle(), length, argv); 433 | } 434 | 435 | void GIRObject::PushInstance(GIRObject *obj, Handle value) 436 | { 437 | Local p_value = value->ToObject(); 438 | obj->MakeWeak(); 439 | 440 | InstanceData data; 441 | data.obj = obj; 442 | data.instance = p_value; 443 | instances.push_back(data); 444 | } 445 | 446 | Handle GIRObject::GetInstance(GObject *obj) 447 | { 448 | std::vector::iterator it; 449 | for (it = instances.begin(); it != instances.end(); it++) { 450 | if (it->obj && it->obj->obj && it->obj->obj == obj) { 451 | return it->instance; 452 | } 453 | } 454 | return Nan::Null(); 455 | } 456 | 457 | void GIRObject::SignalCallback(GClosure *closure, 458 | GValue *return_value, 459 | guint n_param_values, 460 | const GValue *param_values, 461 | gpointer invocation_hint, 462 | gpointer marshal_data) 463 | { 464 | MarshalData *data = (MarshalData*)marshal_data; 465 | 466 | Local args[n_param_values+1]; 467 | //printf ("SignalCallback : [%p] '%s' \n", data->event_name, data->event_name); 468 | args[0] = Nan::New(data->event_name).ToLocalChecked(); 469 | 470 | for (guint i=0; i res = data->that->Emit(args, n_param_values+1); 476 | if (res != Nan::Null()) { 477 | //printf ("Call ToGValue '%s'\n", G_VALUE_TYPE_NAME(return_value)); 478 | if (return_value && G_IS_VALUE(return_value)) 479 | GIRValue::ToGValue(res, G_VALUE_TYPE(return_value), return_value); 480 | } 481 | } 482 | 483 | void GIRObject::SignalFinalize(gpointer marshal_data, GClosure *c) 484 | { 485 | MarshalData *data = (MarshalData*)marshal_data; 486 | g_free (data->event_name); 487 | g_free (data); 488 | } 489 | 490 | NAN_METHOD(GIRObject::CallUnknownMethod) 491 | { 492 | String::Utf8Value fname(info.Callee()->GetName()); 493 | debug_printf("Call method '%s' \n", *fname); 494 | GIRObject *that = Nan::ObjectWrap::Unwrap(info.This()->ToObject()); 495 | GIFunctionInfo *func = that->FindMethod(that->info, *fname); 496 | debug_printf("Call Method: '%s' [%p] \n", *fname, func); 497 | 498 | if (func) { 499 | debug_printf("\t Call symbol: '%s' \n", g_function_info_get_symbol(func)); 500 | info.GetReturnValue().Set(Func::Call(that->obj, func, info, TRUE)); 501 | } 502 | else { 503 | Nan::ThrowError("no such method"); 504 | } 505 | } 506 | 507 | NAN_METHOD(GIRObject::CallMethod) 508 | { 509 | if (info.Length() < 1 || !info[0]->IsString()) { 510 | Nan::ThrowError("Invalid argument's number or type"); 511 | } 512 | 513 | String::Utf8Value fname(info[0]); 514 | GIRObject *that = Nan::ObjectWrap::Unwrap(info.This()->ToObject()); 515 | GIFunctionInfo *func = that->FindMethod(that->info, *fname); 516 | 517 | if (func) { 518 | info.GetReturnValue().Set(Func::Call(that->obj, func, info, FALSE)); 519 | } 520 | else { 521 | Nan::ThrowError("no such method"); 522 | } 523 | 524 | info.GetReturnValue().SetUndefined(); 525 | } 526 | 527 | NAN_METHOD(GIRObject::GetProperty) 528 | { 529 | if (info.Length() < 1 || !info[0]->IsString()) { 530 | Nan::ThrowError("Invalid argument's number or type"); 531 | } 532 | 533 | String::Utf8Value propname(info[0]); 534 | GIRObject *that = Nan::ObjectWrap::Unwrap(info.This()->ToObject()); 535 | GIPropertyInfo *prop = that->FindProperty(that->info, *propname); 536 | 537 | if (!prop) { 538 | Nan::ThrowError("no such property"); 539 | } 540 | if (!(g_property_info_get_flags(prop) & G_PARAM_READABLE)) { 541 | Nan::ThrowError("property is not readable"); 542 | } 543 | 544 | GParamSpec *spec = g_object_class_find_property(G_OBJECT_GET_CLASS(that->obj), *propname); 545 | 546 | GValue gvalue = {0, {{0}}}; 547 | g_value_init(&gvalue, spec->value_type); 548 | g_object_get_property(G_OBJECT(that->obj), *propname, &gvalue); 549 | 550 | Local res = GIRValue::FromGValue(&gvalue, nullptr); 551 | g_value_unset(&gvalue); 552 | 553 | info.GetReturnValue().Set(res); 554 | } 555 | 556 | NAN_METHOD(GIRObject::SetProperty) 557 | { 558 | if(info.Length() < 2 || !info[0]->IsString()) { 559 | Nan::ThrowError("Invalid argument's number or type"); 560 | } 561 | 562 | String::Utf8Value propname(info[0]); 563 | GIRObject *that = Nan::ObjectWrap::Unwrap(info.This()->ToObject()); 564 | GIPropertyInfo *prop = that->FindProperty(that->info, *propname); 565 | 566 | if (!prop) { 567 | Nan::ThrowError("no such property"); 568 | } 569 | if (!(g_property_info_get_flags(prop) & G_PARAM_WRITABLE)) { 570 | Nan::ThrowError("property is not writable"); 571 | } 572 | 573 | GParamSpec *spec = g_object_class_find_property(G_OBJECT_GET_CLASS(that->obj), *propname); 574 | 575 | GValue gvalue = {0, {{0}}}; 576 | if (!GIRValue::ToGValue(info[1], spec->value_type, &gvalue)) { 577 | Nan::ThrowError("Cant convert to JS value to c value"); 578 | } 579 | g_object_set_property(G_OBJECT(that->obj), *propname, &gvalue); 580 | 581 | info.GetReturnValue().SetUndefined(); 582 | } 583 | 584 | NAN_METHOD(GIRObject::GetInterface) 585 | { 586 | if (info.Length() < 1 || !info[0]->IsString()) { 587 | Nan::ThrowError("Invalid argument's number or type"); 588 | } 589 | 590 | String::Utf8Value iname(info[0]); 591 | GIRObject *that = Nan::ObjectWrap::Unwrap(info.This()->ToObject()); 592 | GIInterfaceInfo *interface = that->FindInterface(that->info, *iname); 593 | 594 | if (interface) { 595 | debug_printf("interface %s exists\n", *iname); 596 | } 597 | else { 598 | debug_printf("interface %s does NOT exist\n", *iname); 599 | } 600 | 601 | info.GetReturnValue().SetUndefined(); 602 | } 603 | 604 | NAN_METHOD(GIRObject::GetField) 605 | { 606 | if (info.Length() < 1 || !info[0]->IsString()) { 607 | Nan::ThrowError("Invalid argument's numer or type"); 608 | } 609 | 610 | String::Utf8Value fname(info[0]); 611 | GIRObject *that = Nan::ObjectWrap::Unwrap(info.This()->ToObject()); 612 | GIFieldInfo *field = that->FindField(that->info, *fname); 613 | 614 | if (field) { 615 | debug_printf("field %s exists\n", *fname); 616 | } 617 | else { 618 | debug_printf("field %s does NOT exist\n", *fname); 619 | } 620 | 621 | info.GetReturnValue().SetUndefined(); 622 | } 623 | 624 | NAN_METHOD(GIRObject::WatchSignal) 625 | { 626 | if (info.Length() < 1 || !info[0]->IsString()) { 627 | Nan::ThrowError("Invalid argument's number or type"); 628 | } 629 | bool after = true; 630 | if (info.Length() > 1 && info[1]->IsBoolean()) { 631 | after = info[1]->ToBoolean()->IsTrue(); 632 | } 633 | 634 | String::Utf8Value sname(info[0]); 635 | GIRObject *that = Nan::ObjectWrap::Unwrap(info.This()->ToObject()); 636 | GISignalInfo *signal = that->FindSignal(that->info, *sname); 637 | //printf ("WATCH : OBJ '%s', SIGNAL '%s' \n", G_OBJECT_TYPE_NAME (that->obj), *sname); 638 | 639 | if(signal) { 640 | MarshalData *data = g_new(MarshalData, 1); 641 | data->that = that; 642 | data->event_name = g_strdup(*sname); 643 | 644 | GClosure *closure = g_cclosure_new(G_CALLBACK(empty_func), nullptr, nullptr); 645 | g_closure_add_finalize_notifier(closure, data, GIRObject::SignalFinalize); 646 | g_closure_set_meta_marshal(closure, data, GIRObject::SignalCallback); 647 | g_signal_connect_closure(that->obj, *sname, closure, after); 648 | } 649 | else { 650 | Nan::ThrowError("no such signal"); 651 | } 652 | 653 | info.GetReturnValue().SetUndefined(); 654 | } 655 | 656 | NAN_METHOD(GIRObject::CallVFunc) 657 | { 658 | if (info.Length() < 1 || !info[0]->IsString()) { 659 | Nan::ThrowError("Invalid argument's number or type"); 660 | } 661 | 662 | String::Utf8Value fname(info[0]); 663 | GIRObject *that = Nan::ObjectWrap::Unwrap(info.This()->ToObject()); 664 | GISignalInfo *vfunc = that->FindSignal(that->info, *fname); 665 | 666 | if (vfunc) { 667 | debug_printf("VFunc %s exists\n", *fname); 668 | } 669 | else { 670 | debug_printf("VFunc %s does NOT exist\n", *fname); 671 | } 672 | 673 | info.GetReturnValue().SetUndefined(); 674 | } 675 | 676 | GIFunctionInfo *GIRObject::FindMethod(GIObjectInfo *info, char *name) 677 | { 678 | GIFunctionInfo *func = g_object_info_find_method(info, name); 679 | 680 | // Find interface method 681 | if (!func) { 682 | int ifaces = g_object_info_get_n_interfaces(info); 683 | for (int i = 0; i < ifaces; i++) { 684 | GIInterfaceInfo *iface_info = g_object_info_get_interface(info, i); 685 | func = g_interface_info_find_method(iface_info, name); 686 | if (func) { 687 | g_base_info_unref(iface_info); 688 | return func; 689 | } 690 | g_base_info_unref(iface_info); 691 | } 692 | } 693 | 694 | if (!func) { 695 | GIObjectInfo *parent = g_object_info_get_parent(info); 696 | func = FindMethod(parent, name); 697 | g_base_info_unref(parent); 698 | } 699 | return func; 700 | } 701 | 702 | GIPropertyInfo *g_object_info_find_property(GIObjectInfo *info, char *name) 703 | { 704 | int l = g_object_info_get_n_properties(info); 705 | for (int i=0; i GIRObject::PropertyList(GIObjectInfo *info) 835 | { 836 | Handle list = Nan::New(); 837 | bool first = true; 838 | int gcounter = 0; 839 | g_base_info_ref(info); 840 | 841 | while (true) { 842 | if (!first) { 843 | GIObjectInfo *parent = g_object_info_get_parent(info); 844 | if (!parent) { 845 | return list; 846 | } 847 | if (strcmp(g_base_info_get_name(parent), g_base_info_get_name(info)) == 0) { 848 | return list; 849 | } 850 | g_base_info_unref(info); 851 | info = parent; 852 | } 853 | 854 | int l = g_object_info_get_n_properties(info); 855 | for (int i=0; iSet(Nan::New(i+gcounter), Nan::New(g_base_info_get_name(prop)).ToLocalChecked()); 858 | g_base_info_unref(prop); 859 | } 860 | gcounter += l; 861 | first = false; 862 | } 863 | 864 | return list; 865 | } 866 | 867 | Handle GIRObject::MethodList(GIObjectInfo *info) 868 | { 869 | Handle list = Nan::New(); 870 | bool first = true; 871 | int gcounter = 0; 872 | g_base_info_ref(info); 873 | 874 | while (true) { 875 | if (!first) { 876 | GIObjectInfo *parent = g_object_info_get_parent(info); 877 | if (!parent) { 878 | return list; 879 | } 880 | if (strcmp( g_base_info_get_name(parent), g_base_info_get_name(info) ) == 0) { 881 | return list; 882 | } 883 | g_base_info_unref(info); 884 | info = parent; 885 | } 886 | 887 | int l = g_object_info_get_n_methods(info); 888 | for (int i=0; iSet(Nan::New(i+gcounter), Nan::New(g_base_info_get_name(func)).ToLocalChecked()); 891 | g_base_info_unref(func); 892 | } 893 | gcounter += l; 894 | first = false; 895 | } 896 | 897 | return list; 898 | } 899 | 900 | void GIRObject::RegisterMethods(Handle target, GIObjectInfo *info, const char *namespace_, Handle t) 901 | { 902 | bool is_object_info = GI_IS_OBJECT_INFO(info); 903 | // Register interface methods 904 | if (is_object_info) { 905 | int ifaces = g_object_info_get_n_interfaces(info); 906 | for (int i = 0; i < ifaces; i++) { 907 | GIInterfaceInfo *iface_info = g_object_info_get_interface(info, i); 908 | // Register prerequisities 909 | int n_pre = g_interface_info_get_n_prerequisites(iface_info); 910 | for (int j = 0; j < n_pre; j++) { 911 | GIBaseInfo *pre_info = g_interface_info_get_prerequisite(iface_info, j); 912 | GIRObject::RegisterMethods(target, pre_info, namespace_, t); 913 | g_base_info_unref(pre_info); 914 | } 915 | GIRObject::RegisterMethods(target, iface_info, namespace_, t); 916 | g_base_info_unref(iface_info); 917 | } 918 | } 919 | 920 | bool first = true; 921 | int gcounter = 0; 922 | g_base_info_ref(info); 923 | 924 | while (true) { 925 | if (!first) { 926 | GIObjectInfo *parent = nullptr; 927 | if (GI_IS_OBJECT_INFO(info)) 928 | parent = g_object_info_get_parent(info); 929 | if (!parent) { 930 | g_base_info_unref(info); 931 | return; 932 | } 933 | if (g_base_info_equal(parent, info)) { 934 | g_base_info_unref(info); 935 | return; 936 | } 937 | g_base_info_unref(info); 938 | info = parent; 939 | } 940 | 941 | int l = 0; 942 | if (is_object_info) { 943 | l = g_object_info_get_n_methods(info); 944 | } else { 945 | l = g_interface_info_get_n_methods(info); 946 | } 947 | 948 | for (int i=0; i callback_func = Nan::New(Func::CallStaticMethod)->GetFunction(); 966 | // Set name 967 | callback_func->SetName(Nan::New(func_name).ToLocalChecked()); 968 | // Create external to hold GIBaseInfo and set it 969 | v8::Handle info_ptr = Nan::New((void*)g_base_info_ref(func)); 970 | callback_func->SetHiddenValue(Nan::New("GIInfo").ToLocalChecked(), info_ptr); 971 | // Set v8 function 972 | t->Set(Nan::New(func_name).ToLocalChecked(), callback_func); 973 | } 974 | g_base_info_unref(func); 975 | } 976 | gcounter += l; 977 | first = false; 978 | } 979 | } 980 | 981 | Handle GIRObject::InterfaceList(GIObjectInfo *info) 982 | { 983 | Handle list = Nan::New(); 984 | bool first = true; 985 | int gcounter = 0; 986 | g_base_info_ref(info); 987 | 988 | while (true) { 989 | if (!first) { 990 | GIObjectInfo *parent = g_object_info_get_parent(info); 991 | if (!parent) { 992 | return list; 993 | } 994 | if (strcmp( g_base_info_get_name(parent), g_base_info_get_name(info) ) == 0) { 995 | return list; 996 | } 997 | g_base_info_unref(info); 998 | info = parent; 999 | } 1000 | 1001 | int l = g_object_info_get_n_interfaces(info); 1002 | for (int i=0; iSet(Nan::New(i+gcounter), Nan::New(g_base_info_get_name(interface)).ToLocalChecked()); 1005 | g_base_info_unref(interface); 1006 | } 1007 | gcounter += l; 1008 | first = false; 1009 | } 1010 | 1011 | return list; 1012 | } 1013 | 1014 | Handle GIRObject::FieldList(GIObjectInfo *info) 1015 | { 1016 | Handle list = Nan::New(); 1017 | bool first = true; 1018 | int gcounter = 0; 1019 | g_base_info_ref(info); 1020 | 1021 | while (true) { 1022 | if (!first) { 1023 | GIObjectInfo *parent = g_object_info_get_parent(info); 1024 | if (!parent) { 1025 | return list; 1026 | } 1027 | if (strcmp( g_base_info_get_name(parent), g_base_info_get_name(info) ) == 0) { 1028 | return list; 1029 | } 1030 | g_base_info_unref(info); 1031 | info = parent; 1032 | } 1033 | 1034 | int l = g_object_info_get_n_fields(info); 1035 | for (int i=0; iSet(Nan::New(i+gcounter), Nan::New(g_base_info_get_name(field)).ToLocalChecked()); 1038 | g_base_info_unref(field); 1039 | } 1040 | gcounter += l; 1041 | first = false; 1042 | } 1043 | 1044 | return list; 1045 | } 1046 | 1047 | Handle GIRObject::SignalList(GIObjectInfo *info) 1048 | { 1049 | Handle list = Nan::New(); 1050 | bool first = true; 1051 | int gcounter = 0; 1052 | g_base_info_ref(info); 1053 | 1054 | while (true) { 1055 | if (!first) { 1056 | GIObjectInfo *parent = g_object_info_get_parent(info); 1057 | if (!parent) { 1058 | return list; 1059 | } 1060 | if (strcmp( g_base_info_get_name(parent), g_base_info_get_name(info)/*"InitiallyUnowned"*/ ) == 0) { 1061 | return list; 1062 | } 1063 | g_base_info_unref(info); 1064 | info = parent; 1065 | } 1066 | 1067 | int l = g_object_info_get_n_signals(info); 1068 | for (int i=0; iSet(Nan::New(i+gcounter), Nan::New(g_base_info_get_name(signal)).ToLocalChecked()); 1071 | g_base_info_unref(signal); 1072 | } 1073 | gcounter += l; 1074 | first = false; 1075 | } 1076 | 1077 | return list; 1078 | } 1079 | 1080 | Handle GIRObject::VFuncList(GIObjectInfo *info) 1081 | { 1082 | Handle list = Nan::New(); 1083 | bool first = true; 1084 | int gcounter = 0; 1085 | g_base_info_ref(info); 1086 | 1087 | while (true) { 1088 | if (!first) { 1089 | GIObjectInfo *parent = g_object_info_get_parent(info); 1090 | if (!parent) { 1091 | return list; 1092 | } 1093 | if (strcmp( g_base_info_get_name(parent), g_base_info_get_name(info) ) == 0) { 1094 | return list; 1095 | } 1096 | g_base_info_unref(info); 1097 | info = parent; 1098 | } 1099 | 1100 | int l = g_object_info_get_n_vfuncs(info); 1101 | for (int i=0; iSet(Nan::New(i+gcounter), Nan::New(g_base_info_get_name(vfunc)).ToLocalChecked()); 1104 | g_base_info_unref(vfunc); 1105 | } 1106 | gcounter += l; 1107 | first = false; 1108 | } 1109 | 1110 | return list; 1111 | } 1112 | 1113 | } 1114 | -------------------------------------------------------------------------------- /src/types/object.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace gir { 10 | 11 | class GIRObject; 12 | 13 | struct ObjectFunctionTemplate { 14 | char *type_name; 15 | GIObjectInfo *info; 16 | v8::Handle function; 17 | GType type; 18 | char *namespace_; 19 | }; 20 | 21 | struct MarshalData { 22 | GIRObject *that; 23 | char *event_name; 24 | }; 25 | 26 | struct InstanceData { 27 | v8::Handle instance; 28 | GIRObject *obj; 29 | }; 30 | 31 | 32 | class GIRObject : public Nan::ObjectWrap { 33 | public: 34 | GIRObject() {}; 35 | GIRObject(GIObjectInfo *info_, int n_params, GParameter *parameters); 36 | virtual ~GIRObject(); 37 | 38 | GObject *obj; 39 | bool abstract; 40 | GIBaseInfo *info; 41 | 42 | static std::vector instances; 43 | static std::vector templates; 44 | 45 | static v8::Handle New(GObject *obj, GIObjectInfo *info); 46 | static v8::Handle New(GObject *obj, GType t); 47 | static NAN_METHOD(New); 48 | 49 | static void Prepare(v8::Handle target, GIObjectInfo *info); 50 | static void SetPrototypeMethods(v8::Local t, char *name); 51 | static void RegisterMethods(v8::Handle target, GIObjectInfo *info, const char *namespace_, v8::Handle t); 52 | 53 | static void Initialize(v8::Handle target, char *namespace_); 54 | 55 | static NAN_METHOD(CallMethod); 56 | static NAN_METHOD(CallUnknownMethod); 57 | static NAN_METHOD(GetProperty); 58 | static NAN_METHOD(SetProperty); 59 | static NAN_METHOD(GetInterface); 60 | static NAN_METHOD(GetField); 61 | static NAN_METHOD(WatchSignal); 62 | static NAN_METHOD(CallVFunc); 63 | 64 | static void PushInstance(GIRObject *obj, v8::Handle); 65 | static v8::Handle GetInstance(GObject *obj); 66 | 67 | static void SignalFinalize(gpointer data, GClosure *c); 68 | static void SignalCallback(GClosure *closure, 69 | GValue *return_value, 70 | guint n_param_values, 71 | const GValue *param_values, 72 | gpointer invocation_hint, 73 | gpointer marshal_data); 74 | v8::Handle Emit(v8::Handle argv[], int length); 75 | 76 | static GIFunctionInfo *FindMethod(GIObjectInfo *inf, char *name); 77 | static GIFunctionInfo *FindProperty(GIObjectInfo *inf, char *name); 78 | static GIFunctionInfo *FindInterface(GIObjectInfo *inf, char *name); 79 | static GIFunctionInfo *FindField(GIObjectInfo *inf, char *name); 80 | static GIFunctionInfo *FindSignal(GIObjectInfo *inf, char *name); 81 | static GIFunctionInfo *FindVFunc(GIObjectInfo *inf, char *name); 82 | 83 | private: 84 | static v8::Handle PropertyList(GIObjectInfo *info); 85 | static v8::Handle MethodList(GIObjectInfo *info); 86 | static v8::Handle InterfaceList(GIObjectInfo *info); 87 | static v8::Handle FieldList(GIObjectInfo *info); 88 | static v8::Handle SignalList(GIObjectInfo *info); 89 | static v8::Handle VFuncList(GIObjectInfo *info); 90 | 91 | static v8::Handle ToParams(v8::Handle val, GParameter** p, int *length, GIObjectInfo *info); 92 | static void DeleteParams(GParameter* params, int length); 93 | }; 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/types/struct.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "struct.h" 4 | #include "function_type.h" 5 | #include "../util.h" 6 | #include "../function.h" 7 | #include "../values.h" 8 | #include "../arguments.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace v8; 15 | using namespace std; 16 | 17 | namespace gir { 18 | 19 | std::vector GIRStruct::templates; 20 | std::vector GIRStruct::instances; 21 | static Persistent emit_symbol; 22 | 23 | GIRStruct::GIRStruct(GIStructInfo *info) 24 | { 25 | //c_structure = g_try_malloc0(g_struct_info_get_size ((GIStructInfo*)info)); 26 | } 27 | 28 | Handle GIRStruct::New(gpointer c_structure, GIStructInfo *info) 29 | { 30 | Handle res = GetStructure(c_structure); 31 | if (res != Nan::Null()) { 32 | return res; 33 | } 34 | 35 | std::vector::iterator it; 36 | 37 | for (it = templates.begin(); it != templates.end(); ++it) { 38 | if (g_base_info_equal(info, it->info)) { 39 | res = it->function->GetFunction()->NewInstance(); 40 | break; 41 | } 42 | } 43 | 44 | if (!res.IsEmpty()) { 45 | GIRStruct *s = ObjectWrap::Unwrap(res->ToObject()); 46 | s->info = info; 47 | if (s->c_structure) 48 | g_free(s->c_structure); 49 | s->c_structure = c_structure; 50 | } 51 | 52 | return res; 53 | } 54 | 55 | NAN_METHOD(GIRStruct::New) 56 | { 57 | String::Utf8Value className(info.Callee()->GetName()); 58 | debug_printf ("Struct constructor '%s' \n", *className); 59 | std::vector::iterator it; 60 | 61 | GIObjectInfo *gobjinfo = nullptr; 62 | for (it = templates.begin(); it != templates.end(); ++it) { 63 | if (strcmp(it->type_name, *className) == 0) { 64 | gobjinfo = it->info; 65 | break; 66 | } 67 | } 68 | 69 | if (gobjinfo == nullptr || !GI_IS_STRUCT_INFO(gobjinfo)) { 70 | Nan::ThrowError("Missed introspection structure info"); 71 | } 72 | 73 | GIBaseInfo *func = (GIBaseInfo*) g_struct_info_find_method(gobjinfo, "new"); 74 | if (func == nullptr) { 75 | Nan::ThrowError("Missed introspection structure constructor info"); 76 | } 77 | 78 | GIArgument retval; 79 | GITypeInfo *returned_type_info; 80 | gint returned_array_length; 81 | Func::CallAndGetPtr(nullptr, func, info, TRUE, &retval, &returned_type_info, &returned_array_length); 82 | 83 | if (returned_type_info != nullptr) 84 | g_base_info_unref(returned_type_info); 85 | 86 | GIRStruct *obj = new GIRStruct(gobjinfo); 87 | 88 | /* Set underlying C structure */ 89 | obj->c_structure = (gpointer) retval.v_pointer; 90 | 91 | obj->Wrap(info.This()); 92 | PushInstance(obj, info.This()); 93 | obj->info = gobjinfo; 94 | 95 | info.GetReturnValue().Set(info.This()); 96 | } 97 | 98 | GIFieldInfo *_find_structure_member(GIStructInfo *info, const gchar *name) 99 | { 100 | gint n_fields = g_struct_info_get_n_fields(info); 101 | for(int i = 0; i < n_fields; i++) { 102 | GIFieldInfo *field = g_struct_info_get_field(info, i); 103 | if (g_str_equal(g_base_info_get_name(field), name)) 104 | return field; 105 | g_base_info_unref(field); 106 | } 107 | return nullptr; 108 | } 109 | 110 | NAN_PROPERTY_GETTER(FieldGetHandler) 111 | { 112 | String::Utf8Value _name(property); 113 | v8::Handle info_ptr = v8::Handle::Cast(info.Data()); 114 | GIBaseInfo *base_info = (GIBaseInfo*) info_ptr->Value(); 115 | GIFieldInfo *field_info = nullptr; 116 | if (base_info != nullptr) 117 | field_info = _find_structure_member(base_info, *_name); 118 | 119 | if (field_info) { 120 | if (!(g_field_info_get_flags(field_info) & GI_FIELD_IS_READABLE)) { 121 | // field is not readable 122 | Nan::ThrowError("member is not readable"); 123 | } 124 | GIRStruct *that = Nan::ObjectWrap::Unwrap(info.This()->ToObject()); 125 | GIArgument arg = {0, }; 126 | Local res; 127 | debug_printf("GetHandler [%p] (Get structure member) '%s.%s' \n", that->c_structure, g_base_info_get_name(base_info), *_name); 128 | GITypeInfo *type_info = g_field_info_get_type(field_info); 129 | if (g_field_info_get_field(field_info, that->c_structure, &arg) == TRUE) { 130 | res = Args::FromGType(&arg, type_info, -1); 131 | } else { 132 | res = Nan::Undefined(); 133 | } 134 | 135 | // TODO free arg.v_string 136 | g_base_info_unref(type_info); 137 | g_base_info_unref(field_info); 138 | 139 | info.GetReturnValue().Set(res); 140 | } 141 | 142 | // Fallback to defaults 143 | info.GetReturnValue().Set(info.This()->GetPrototype()->ToObject()->Get(property)); 144 | } 145 | 146 | NAN_PROPERTY_QUERY(FieldQueryHandler) 147 | { 148 | String::Utf8Value _name(property); 149 | debug_printf("QUERY HANDLER '%s' \n", *_name); 150 | info.GetReturnValue().Set(Nan::New(0)); 151 | } 152 | 153 | NAN_PROPERTY_SETTER(FieldSetHandler) 154 | { 155 | String::Utf8Value _name(property); 156 | 157 | v8::Handle info_ptr = v8::Handle::Cast(info.Data()); 158 | GIBaseInfo *base_info = (GIBaseInfo*) info_ptr->Value(); 159 | GIFieldInfo *field_info = nullptr; 160 | if (base_info != nullptr) 161 | field_info = _find_structure_member(base_info, *_name); 162 | 163 | if (field_info) { 164 | if (!(g_field_info_get_flags(field_info) & GI_FIELD_IS_WRITABLE)) { 165 | // field is not writable 166 | Nan::ThrowError("member is not writable"); 167 | } 168 | 169 | GIRStruct *that = Nan::ObjectWrap::Unwrap(info.This()->ToObject()); 170 | debug_printf("SetHandler [%p] (Set structure member) '%s.%s' \n", that->c_structure, g_base_info_get_name(base_info), *_name); 171 | GIArgument arg = {0, }; 172 | Handle res; 173 | GITypeInfo *type_info = g_field_info_get_type(field_info); 174 | // FIXME, add TypeInfo argument when ArgInfo is nullptr 175 | bool is_set = Args::ToGType(value, &arg, nullptr, type_info, false); 176 | if (g_field_info_set_field(field_info, that->c_structure, &arg) == false) { 177 | Nan::ThrowError("Failed to set structure's field"); 178 | } 179 | 180 | /*GIArgument ar = {0, }; 181 | if (g_field_info_get_field(field_info, that->c_structure, &ar) == TRUE) { 182 | Handle ret = Args::FromGType(&arg, type_info, -1); 183 | } */ 184 | // TODO, free arg.v_string 185 | g_base_info_unref(type_info); 186 | g_base_info_unref(field_info); 187 | 188 | info.GetReturnValue().Set(Nan::New(is_set)); 189 | } 190 | 191 | // Fallback to defaults 192 | info.GetReturnValue().Set(Nan::New(info.This()->GetPrototype()->ToObject()->Set(property, value))); 193 | } 194 | 195 | void GIRStruct::Prepare(Handle target, GIStructInfo *info) 196 | { 197 | char *name = (char*)g_base_info_get_name(info); 198 | const char *namespace_ = g_base_info_get_namespace(info); 199 | g_base_info_ref(info); 200 | 201 | // Ignore, if this is gtype (object class) struct. 202 | if (g_struct_info_is_gtype_struct(info)) 203 | return; 204 | 205 | // Ignore all Private and IFace structures 206 | if (g_str_has_suffix(name, "Private") 207 | || g_str_has_suffix(name, "IFace")) 208 | return; 209 | 210 | Local t = Nan::New(New); 211 | t->SetClassName(Nan::New(name).ToLocalChecked()); 212 | 213 | StructFunctionTemplate oft; 214 | oft.type_name = name; 215 | oft.info = info; 216 | oft.function = t; 217 | oft.type = g_registered_type_info_get_g_type(info); 218 | oft.namespace_ = (char*)namespace_; 219 | 220 | templates.push_back(oft); 221 | 222 | // Create instance template 223 | v8::Local instance_t = t->InstanceTemplate(); 224 | instance_t->SetInternalFieldCount(1); 225 | // Create external to hold GIBaseInfo and set it 226 | v8::Handle info_handle = Nan::New((void*)g_base_info_ref(info)); 227 | // Set fields handlers 228 | SetNamedPropertyHandler(instance_t, FieldGetHandler, FieldSetHandler, FieldQueryHandler, 0, 0, info_handle); 229 | 230 | RegisterMethods(target, info, namespace_, t); 231 | } 232 | 233 | void GIRStruct::Initialize(Handle target, char *namespace_) 234 | { 235 | // this gets called when all structures have been initialized 236 | std::vector::iterator it; 237 | std::vector::iterator temp; 238 | 239 | for (it = templates.begin(); it != templates.end(); ++it) { 240 | if (strcmp(it->namespace_, namespace_) == 0) { 241 | target->Set(Nan::New(g_base_info_get_name(it->info)).ToLocalChecked(), it->function->GetFunction()); 242 | } 243 | } 244 | } 245 | 246 | void GIRStruct::PushInstance(GIRStruct *obj, Handle value) 247 | { 248 | Local p_value = value->ToObject(); 249 | obj->MakeWeak(); 250 | 251 | StructData data; 252 | data.gir_structure = obj; 253 | data.instance = p_value; 254 | instances.push_back(data); 255 | } 256 | 257 | Handle GIRStruct::GetStructure(gpointer c_structure) 258 | { 259 | std::vector::iterator it; 260 | for (it = instances.begin(); it != instances.end(); it++) { 261 | if (it->gir_structure && it->gir_structure->c_structure && it->gir_structure->c_structure == c_structure) { 262 | return it->instance; 263 | } 264 | } 265 | return Nan::Null(); 266 | } 267 | 268 | NAN_METHOD(GIRStruct::CallMethod) 269 | { 270 | String::Utf8Value fname(info.Callee()->GetName()); 271 | GIRStruct *that = Nan::ObjectWrap::Unwrap(info.This()->ToObject()); 272 | if (!GI_IS_STRUCT_INFO(that->info)) { 273 | Nan::ThrowError("Missed structure info to call method"); 274 | } 275 | GIFunctionInfo *func = g_struct_info_find_method(that->info, *fname); 276 | debug_printf("Call Method: '%s' [%p] \n", *fname, func); 277 | if (func) { 278 | debug_printf("\t Call symbol: '%s' \n", g_function_info_get_symbol(func)); 279 | info.GetReturnValue().Set(Func::Call((GObject *)that->c_structure, func, info, TRUE)); 280 | } 281 | else { 282 | Nan::ThrowError("no such method"); 283 | } 284 | info.GetReturnValue().SetUndefined(); 285 | } 286 | 287 | Handle GIRStruct::PropertyList(GIObjectInfo *info) 288 | { 289 | Handle list = Nan::New(); 290 | bool first = true; 291 | int gcounter = 0; 292 | g_base_info_ref(info); 293 | 294 | while (true) { 295 | if (!first) { 296 | GIObjectInfo *parent = g_object_info_get_parent(info); 297 | if (!parent) { 298 | return list; 299 | } 300 | if (strcmp(g_base_info_get_name(parent), g_base_info_get_name(info)) == 0) { 301 | return list; 302 | } 303 | g_base_info_unref(info); 304 | info = parent; 305 | } 306 | 307 | int l = g_object_info_get_n_properties(info); 308 | for (int i=0; iSet(Nan::New(i+gcounter), Nan::New(g_base_info_get_name(prop)).ToLocalChecked()); 311 | g_base_info_unref(prop); 312 | } 313 | gcounter += l; 314 | first = false; 315 | } 316 | 317 | return list; 318 | } 319 | 320 | Handle GIRStruct::MethodList(GIObjectInfo *info) 321 | { 322 | Handle list = Nan::New(); 323 | bool first = true; 324 | int gcounter = 0; 325 | g_base_info_ref(info); 326 | 327 | while (true) { 328 | if (!first) { 329 | GIObjectInfo *parent = g_object_info_get_parent(info); 330 | if (!parent) { 331 | return list; 332 | } 333 | if (strcmp( g_base_info_get_name(parent), g_base_info_get_name(info) ) == 0) { 334 | return list; 335 | } 336 | g_base_info_unref(info); 337 | info = parent; 338 | } 339 | 340 | int l = g_object_info_get_n_methods(info); 341 | for (int i=0; iSet(Nan::New(i+gcounter), Nan::New(g_base_info_get_name(func)).ToLocalChecked()); 344 | g_base_info_unref(func); 345 | } 346 | gcounter += l; 347 | first = false; 348 | } 349 | 350 | return list; 351 | } 352 | 353 | void GIRStruct::RegisterMethods(Handle target, GIStructInfo *info, const char *namespace_, Handle t) 354 | { 355 | int l = g_struct_info_get_n_methods(info); 356 | for (int i=0; i callback_func = Nan::New(Func::CallStaticMethod)->GetFunction(); 369 | // Set name 370 | callback_func->SetName(Nan::New(func_name).ToLocalChecked()); 371 | // Create external to hold GIBaseInfo and set it 372 | v8::Handle info_ptr = Nan::New((void*)g_base_info_ref(func)); 373 | callback_func->SetHiddenValue(Nan::New("GIInfo").ToLocalChecked(), info_ptr); 374 | // Set v8 function 375 | t->Set(Nan::New(func_name).ToLocalChecked(), callback_func); 376 | //printf ("REGISTER STRUCT CTR '%s' \n", g_function_info_get_symbol (func)); 377 | } else { 378 | Nan::SetPrototypeMethod(t, func_name, CallMethod); 379 | //printf ("REGISTER STRUCT METHOD '%s' \n", g_function_info_get_symbol (func)); 380 | } 381 | g_base_info_unref(func); 382 | } 383 | } 384 | 385 | Handle GIRStruct::FieldList(GIObjectInfo *info) 386 | { 387 | Handle list = Nan::New(); 388 | bool first = true; 389 | int gcounter = 0; 390 | g_base_info_ref(info); 391 | 392 | while (true) { 393 | if (!first) { 394 | GIObjectInfo *parent = g_object_info_get_parent(info); 395 | if (!parent) { 396 | return list; 397 | } 398 | if (strcmp( g_base_info_get_name(parent), g_base_info_get_name(info) ) == 0) { 399 | return list; 400 | } 401 | g_base_info_unref(info); 402 | info = parent; 403 | } 404 | 405 | int l = g_object_info_get_n_fields(info); 406 | for (int i=0; iSet(Nan::New(i+gcounter), Nan::New(g_base_info_get_name(field)).ToLocalChecked()); 409 | g_base_info_unref(field); 410 | } 411 | gcounter += l; 412 | first = false; 413 | } 414 | 415 | return list; 416 | } 417 | 418 | } 419 | -------------------------------------------------------------------------------- /src/types/struct.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace gir { 10 | 11 | class GIRStruct; 12 | 13 | struct StructFunctionTemplate { 14 | char *type_name; 15 | GIObjectInfo *info; 16 | v8::Handle function; 17 | GType type; 18 | char *namespace_; 19 | }; 20 | 21 | struct StructMarshalData { 22 | GIRStruct *that; 23 | char *event_name; 24 | }; 25 | 26 | struct StructData { 27 | v8::Handle instance; 28 | GIRStruct *gir_structure; 29 | }; 30 | 31 | class GIRStruct : public Nan::ObjectWrap { 32 | public: 33 | GIRStruct() {}; 34 | GIRStruct(GIObjectInfo *info); 35 | 36 | gpointer c_structure; 37 | bool abstract; 38 | GIBaseInfo *info; 39 | 40 | static std::vector instances; 41 | static std::vector templates; 42 | 43 | static v8::Handle New(gpointer c_structure, GIStructInfo *info); 44 | static NAN_METHOD(New); 45 | 46 | static void Prepare(v8::Handle target, GIObjectInfo *info); 47 | static void RegisterMethods(v8::Handle target, GIObjectInfo *info, const char *namespace_, v8::Handle t); 48 | 49 | static void Initialize(v8::Handle target, char *namespace_); 50 | 51 | static NAN_METHOD(CallMethod); 52 | 53 | static void PushInstance(GIRStruct *obj, v8::Handle); 54 | static v8::Handle GetStructure(gpointer c_structure); 55 | 56 | 57 | private: 58 | static v8::Handle PropertyList(GIObjectInfo *info); 59 | static v8::Handle MethodList(GIObjectInfo *info); 60 | static v8::Handle InterfaceList(GIObjectInfo *info); 61 | static v8::Handle FieldList(GIObjectInfo *info); 62 | }; 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/util.cc: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include 3 | #include 4 | #include 5 | 6 | static char *_format_message(const char *fmt, va_list args) 7 | { 8 | if(!fmt) 9 | return g_strdup(""); 10 | 11 | return g_strdup_vprintf(fmt, args); 12 | } 13 | 14 | extern "C" void debug_printf(const char *fmt, ...) 15 | { 16 | const char *debug = getenv("NODE_GIR_DEBUG"); 17 | if (debug == NULL || *debug == '\0') { 18 | return; 19 | } 20 | printf(" DEBUG: "); 21 | 22 | va_list args; 23 | va_start (args, fmt); 24 | char *msg = _format_message(fmt, args); 25 | va_end(args); 26 | 27 | printf("%s", msg); 28 | g_free(msg); 29 | } 30 | 31 | namespace gir { 32 | namespace Util { 33 | 34 | gchar *utf8StringFromValue(v8::Handle value) 35 | { 36 | v8::Local string = value->ToString(); 37 | gchar *buffer = g_new(gchar, string->Utf8Length()); 38 | string->WriteUtf8(buffer); 39 | return buffer; 40 | } 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define GIR_DEFINE_CONSTANT(target, name, constant) \ 7 | (target)->Set(v8::String::NewSymbol(name), \ 8 | v8::Integer::New(constant), \ 9 | static_cast(v8::ReadOnly|v8::DontDelete)) 10 | 11 | #define BAD_ARGS(_msg) \ 12 | Nan::ThrowTypeError(_msg); 13 | 14 | #define NO_UNDERLYING_OBJECT() \ 15 | Nan::ThrowTypeError("no underlying object found"); 16 | 17 | #define EXCEPTION(str) \ 18 | Nan::ThrowTypeError(str); 19 | 20 | extern "C" void debug_printf(const char *fmt, ...); 21 | 22 | namespace gir { 23 | 24 | namespace Util { 25 | gchar *utf8StringFromValue(v8::Handle value); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/values.cc: -------------------------------------------------------------------------------- 1 | #include "values.h" 2 | #include "util.h" 3 | #include "namespace_loader.h" 4 | 5 | #include "types/object.h" 6 | #include "types/struct.h" 7 | #include 8 | 9 | using namespace v8; 10 | 11 | namespace gir { 12 | 13 | Handle GIRValue::FromGValue(GValue *v, GIBaseInfo *base_info) { 14 | GType type = G_VALUE_TYPE(v); 15 | Handle value = Nan::Undefined(); 16 | const char *tmpstr; 17 | char *str; 18 | GIBaseInfo *boxed_info; 19 | 20 | switch (G_TYPE_FUNDAMENTAL(type)) { 21 | case G_TYPE_CHAR: 22 | str = new char[2]; 23 | str[0] = g_value_get_schar(v); 24 | str[1] = '\0'; 25 | value = Nan::New(str).ToLocalChecked(); 26 | delete[] str; 27 | return value; 28 | 29 | case G_TYPE_UCHAR: 30 | str = new char[2]; 31 | str[0] = g_value_get_uchar(v); 32 | str[1] = '\0'; 33 | value = Nan::New(str).ToLocalChecked(); 34 | delete[] str; 35 | return value; 36 | 37 | case G_TYPE_BOOLEAN: 38 | return Nan::New(g_value_get_boolean(v)); 39 | 40 | case G_TYPE_INT: 41 | return Nan::New(g_value_get_int(v)); 42 | 43 | case G_TYPE_UINT: 44 | return Nan::New(g_value_get_uint(v)); 45 | 46 | case G_TYPE_LONG: 47 | return Nan::New(g_value_get_long(v)); 48 | 49 | case G_TYPE_ULONG: 50 | return Nan::New(g_value_get_ulong(v)); 51 | 52 | case G_TYPE_INT64: 53 | return Nan::New(g_value_get_int64(v)); 54 | 55 | case G_TYPE_UINT64: 56 | return Nan::New(g_value_get_uint64(v)); 57 | 58 | case G_TYPE_ENUM: 59 | return Nan::New(g_value_get_enum(v)); 60 | 61 | case G_TYPE_FLAGS: 62 | return Nan::New(g_value_get_flags(v)); 63 | 64 | case G_TYPE_FLOAT: 65 | return Nan::New(g_value_get_float(v)); 66 | 67 | case G_TYPE_DOUBLE: 68 | return Nan::New(g_value_get_double(v)); 69 | 70 | case G_TYPE_STRING: 71 | tmpstr = g_value_get_string(v); 72 | return Nan::New(tmpstr ? tmpstr : "").ToLocalChecked(); 73 | 74 | case G_TYPE_BOXED: 75 | if (G_VALUE_TYPE(v) == G_TYPE_ARRAY) { 76 | Nan::ThrowError("GIRValue - GValueArray conversion not supported"); 77 | } else { 78 | // Handle C structure held by boxed type 79 | if (base_info == NULL) 80 | Nan::ThrowError("GIRValue - missed base_info for boxed type"); 81 | boxed_info = g_irepository_find_by_gtype(NamespaceLoader::repo, G_VALUE_TYPE(v)); 82 | return GIRStruct::New((GIRStruct*)g_value_get_boxed(v), boxed_info); 83 | } 84 | 85 | case G_TYPE_OBJECT: 86 | return GIRObject::New(G_OBJECT(g_value_get_object(v)), type); 87 | 88 | default: 89 | Nan::ThrowError("GIRValue - conversion of '%s' type not supported"); 90 | } 91 | 92 | return value; 93 | } 94 | 95 | bool GIRValue::ToGValue(Handle value, GType type, GValue *v) { 96 | if(type == G_TYPE_INVALID || type == 0) { 97 | type = GIRValue::GuessType(value); 98 | } 99 | if(type == 0) { 100 | return false; 101 | } 102 | 103 | g_value_init(v, type); 104 | 105 | switch (G_TYPE_FUNDAMENTAL(type)) { 106 | case G_TYPE_INTERFACE: 107 | case G_TYPE_OBJECT: 108 | if (value->IsObject()) { 109 | g_value_set_object(v, Nan::ObjectWrap::Unwrap(value->ToObject())->obj); 110 | return true; 111 | } 112 | 113 | case G_TYPE_CHAR: 114 | if(value->IsString()) { 115 | String::Utf8Value str(value); 116 | g_value_set_schar(v, (*str)[0]); 117 | return true; 118 | } 119 | 120 | case G_TYPE_UCHAR: 121 | if(value->IsString()) { 122 | String::Utf8Value str(value); 123 | g_value_set_schar(v, (*str)[0]); 124 | return true; 125 | } 126 | 127 | case G_TYPE_BOOLEAN: 128 | if(value->IsBoolean()) { 129 | g_value_set_boolean(v, value->ToBoolean()->IsTrue()); 130 | return true; 131 | } 132 | 133 | case G_TYPE_INT: 134 | if(value->IsNumber()) { 135 | g_value_set_int(v, (gint)value->ToInt32()->Value()); 136 | return true; 137 | } 138 | 139 | case G_TYPE_UINT: 140 | if(value->IsNumber()) { 141 | g_value_set_uint(v, value->NumberValue()); 142 | return true; 143 | } 144 | 145 | case G_TYPE_LONG: 146 | if(value->IsNumber()) { 147 | g_value_set_long(v, value->NumberValue()); 148 | return true; 149 | } 150 | 151 | case G_TYPE_ULONG: 152 | if(value->IsNumber()) { 153 | g_value_set_ulong(v, value->NumberValue()); 154 | return true; 155 | } 156 | 157 | case G_TYPE_INT64: 158 | if(value->IsNumber()) { 159 | g_value_set_int64(v, value->NumberValue()); 160 | return true; 161 | } 162 | 163 | case G_TYPE_UINT64: 164 | if(value->IsNumber()) { 165 | g_value_set_uint64(v, value->NumberValue()); 166 | return true; 167 | } 168 | 169 | case G_TYPE_ENUM: 170 | if(value->IsNumber()) { 171 | g_value_set_enum(v, value->NumberValue()); 172 | return true; 173 | } 174 | 175 | case G_TYPE_FLAGS: 176 | if(value->IsNumber()) { 177 | g_value_set_flags(v, value->NumberValue()); 178 | return true; 179 | } 180 | 181 | case G_TYPE_FLOAT: 182 | if(value->IsNumber()) { 183 | g_value_set_float(v, value->NumberValue()); 184 | return true; 185 | } 186 | 187 | case G_TYPE_DOUBLE: 188 | if(value->IsNumber()) { 189 | g_value_set_double(v, (gdouble)value->NumberValue()); 190 | return true; 191 | } 192 | 193 | case G_TYPE_STRING: 194 | if(value->IsString()) { 195 | g_value_set_string(v, *String::Utf8Value(value->ToString()) ); 196 | return true; 197 | } 198 | 199 | case G_TYPE_POINTER: 200 | break; 201 | 202 | case G_TYPE_BOXED: 203 | g_value_set_boxed(v, Nan::ObjectWrap::Unwrap(value->ToObject())->c_structure); 204 | return true; 205 | break; 206 | 207 | case G_TYPE_PARAM: 208 | break; 209 | 210 | default: 211 | Nan::ThrowError("Failed to convert value"); 212 | return false; 213 | break; 214 | } 215 | return false; 216 | } 217 | 218 | GType GIRValue::GuessType(Handle value) { 219 | if(value->IsString()) { 220 | return G_TYPE_STRING; 221 | } 222 | else if(value->IsArray()) { 223 | return G_TYPE_ARRAY; 224 | } 225 | else if(value->IsBoolean()) { 226 | return G_TYPE_BOOLEAN; 227 | } 228 | else if(value->IsInt32()) { 229 | return G_TYPE_INT; 230 | } 231 | else if(value->IsUint32()) { 232 | return G_TYPE_UINT; 233 | } 234 | else if(value->IsNumber()) { 235 | return G_TYPE_DOUBLE; 236 | } 237 | return G_TYPE_INVALID; 238 | } 239 | 240 | } 241 | -------------------------------------------------------------------------------- /src/values.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace gir { 8 | 9 | class GIRValue { 10 | public: 11 | static v8::Handle FromGValue(GValue *v, GIBaseInfo *base_info); 12 | static bool ToGValue(v8::Handle value, GType t, GValue *v); 13 | static GType GuessType(v8::Handle value); 14 | }; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /tests/TestGIR.js: -------------------------------------------------------------------------------- 1 | var Gir = require('../gir'); 2 | 3 | var Gtk = Gir.load('Gtk', '3.0'); 4 | Gtk.init(0); 5 | 6 | var GLib = Gir.load('GLib'); 7 | var GObject = Gir.load('GObject'); 8 | var GdkPixbuf = Gir.load('GdkPixbuf'); 9 | 10 | exports.GdkPixbuf = GdkPixbuf; 11 | exports.GLib = GLib; 12 | exports.GObject = GObject; 13 | exports.Gtk = Gtk; 14 | exports.win = new Gtk.Window({type: Gtk.WindowType.toplevel, title:"Node.JS GTK Window"}); 15 | -------------------------------------------------------------------------------- /tests/midgard/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | npm install vows 4 | npm install gir 5 | # Create symlink in case npm is too old 6 | ln -s default node_modules/gir/build/Release 7 | -------------------------------------------------------------------------------- /tests/midgard/midgard_connection.js: -------------------------------------------------------------------------------- 1 | 2 | var gir = require('../../gir'); 3 | gir.init(); 4 | 5 | var Midgard, gir, config, mgd; 6 | Midgard = gir.load('Midgard'); 7 | Midgard.init(); 8 | 9 | config = new Midgard.Config(); 10 | config.read_file_at_path(__dirname + '/test_SQLITE.conf'); 11 | 12 | mgd = new Midgard.Connection(); 13 | mgd.open_config(config); 14 | 15 | Midgard.Storage.create_base_storage(mgd); 16 | 17 | var test_midgard_bookstore = Midgard.Object.factory(mgd, "gir_test_book_store"); 18 | test_midgard_bookstore.name = "BookStore"; 19 | //test_midgard_bookstore.create(); 20 | 21 | var test_midgard_book = Midgard.Object.factory(mgd, "gir_test_book_crud"); 22 | test_midgard_book.title = "The Holly Grail"; 23 | test_midgard_book.author = "Sir Lancelot"; 24 | test_midgard_book.price = 99.99; 25 | test_midgard_book.serial = Midgard.Guid.new(mgd); 26 | test_midgard_book.edition = 1; 27 | test_midgard_book.sold = false; 28 | test_midgard_book.description = "The true story of white rabbit"; 29 | test_midgard_book.store = test_midgard_bookstore.id; 30 | 31 | exports.TestBookStore = test_midgard_bookstore; 32 | exports.TestBook = test_midgard_book; 33 | exports.cnc = mgd; 34 | exports.TimeStamp = new Midgard.Timestamp(); 35 | -------------------------------------------------------------------------------- /tests/midgard/test_000_config.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var gir = require('../../gir'); 6 | gir.init(); 7 | 8 | var GObject = gir.load('GObject'); 9 | var Midgard = gir.load('Midgard'); 10 | Midgard.init(); 11 | 12 | config = new Midgard.Config(); 13 | config.dbdir = '/tmp'; 14 | config.dbtype = 'SQLite'; 15 | config.database = 'node_gir'; 16 | 17 | mgd = new Midgard.Connection(); 18 | 19 | describe('Midgard.Config', function() { 20 | 21 | it('Open config', function() { 22 | var opened = mgd.open_config(config); 23 | opened.should.be.a('boolean'); 24 | opened.should.equal(true); 25 | }); 26 | }); 27 | 28 | -------------------------------------------------------------------------------- /tests/midgard/test_300_storage.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var gir = require('../../gir'); 6 | gir.init(); 7 | 8 | var GObject = gir.load('GObject'); 9 | var Midgard = gir.load('Midgard'); 10 | 11 | var MidgardTest = require('./midgard_connection'); 12 | 13 | describe('Midgard.Storage', function() { 14 | 15 | it('create base storage', function() { 16 | var created = Midgard.Storage.create_base_storage(MidgardTest.cnc); 17 | created.should.equal(true); 18 | }); 19 | 20 | it('create MgdSchema storage', function() { 21 | var type = GObject.type_from_name('MidgardObject'); 22 | var children = GObject.type_children(type); 23 | var i = 0; 24 | for (i in children) { 25 | console.log("Create " + GObject.type_name(children[i]) + " storage"); 26 | var created = Midgard.Storage.create(MidgardTest.cnc, GObject.type_name(children[i])); 27 | created.should.equal(true); 28 | } 29 | }); 30 | 31 | it('create DBObject storage', function() { 32 | var type = GObject.type_from_name('MidgardDBObject'); 33 | var children = GObject.type_children(type); 34 | var i = 0; 35 | var ignored = {'MidgardMetadata':1, 'MidgardObject':1, 'MidgardView':1}; 36 | for (i in children) { 37 | var typename = GObject.type_name(children[i]); 38 | if (ignored[typename]) { 39 | continue; 40 | } 41 | //console.log("Create " + GObject.type_name(children[i]) + " storage"); 42 | var created = Midgard.Storage.create(MidgardTest.cnc, GObject.type_name(children[i])); 43 | created.should.equal(true); 44 | } 45 | }); 46 | 47 | it('update MgdSchema storage', function() { 48 | var type = GObject.type_from_name('MidgardObject'); 49 | var children = GObject.type_children(type); 50 | var i = 0; 51 | for (i in children) { 52 | //console.log("Create " + GObject.type_name(children[i]) + " storage"); 53 | var updated = Midgard.Storage.update(MidgardTest.cnc, GObject.type_name(children[i])); 54 | updated.should.equal(true); 55 | } 56 | }); 57 | 58 | it('update DBObject storage', function() { 59 | var type = GObject.type_from_name('MidgardDBObject'); 60 | var children = GObject.type_children(type); 61 | var i = 0; 62 | var ignored = {'MidgardMetadata':1, 'MidgardObject':1, 'MidgardView':1}; 63 | for (i in children) { 64 | var typename = GObject.type_name(children[i]); 65 | if (ignored[typename]) { 66 | continue; 67 | } 68 | //console.log("Create " + GObject.type_name(children[i]) + " storage"); 69 | var updated = Midgard.Storage.create(MidgardTest.cnc, GObject.type_name(children[i])); 70 | updated.should.equal(true); 71 | } 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /tests/midgard/test_400_timestamp.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var gir = require('../../gir'); 6 | gir.init(); 7 | 8 | var GObject = gir.load('GObject'); 9 | var Midgard = gir.load('Midgard'); 10 | Midgard.init(); 11 | 12 | var MidgardTest = require('./midgard_connection'); 13 | 14 | describe('Midgard.TimeStamp', function() { 15 | 16 | it('new from iso', function() { 17 | // It should be resolved ni midgard-core 18 | // e.g. Midgard.TimeStamp.create_from_iso.... 19 | //var datetime = "2012-06-07 14:35:00+0000"; 20 | //var ts = Midgard.TimeStamp.new_from_iso8601(datetime); 21 | //ts.get_string().should.equal(datetime); 22 | }); 23 | 24 | describe('Get default values', function() { 25 | 26 | it ('year', function() { 27 | var v = MidgardTest.TimeStamp; 28 | v.year.should.be.a('number'); 29 | v.year.should.equal(1); 30 | }); 31 | 32 | it ('month', function() { 33 | var v = MidgardTest.TimeStamp; 34 | v.month.should.be.a('number'); 35 | v.month.should.equal(1); 36 | }); 37 | 38 | it ('day', function() { 39 | var v = MidgardTest.TimeStamp; 40 | v.day.should.be.a('number'); 41 | v.day.should.equal(1); 42 | }); 43 | 44 | it ('hour', function() { 45 | var v = MidgardTest.TimeStamp; 46 | v.hour.should.be.a('number'); 47 | v.hour.should.equal(0); 48 | }); 49 | 50 | it ('minute', function() { 51 | var v = MidgardTest.TimeStamp; 52 | v.minute.should.be.a('number'); 53 | v.minute.should.equal(0); 54 | }); 55 | 56 | it ('second', function() { 57 | var v = MidgardTest.TimeStamp; 58 | v.second.should.be.a('number'); 59 | v.second.should.equal(0); 60 | }); 61 | 62 | it ('get string', function() { 63 | var v = MidgardTest.TimeStamp; 64 | v.get_string().should.be.a('string'); 65 | v.get_string().should.equal('0001-01-01 00:00:00+0000'); 66 | }); 67 | }); 68 | 69 | describe('Set values', function() { 70 | 71 | it ('year', function() { 72 | var v = MidgardTest.TimeStamp; 73 | v.year = 2012; 74 | v.year.should.be.a('number'); 75 | v.year.should.equal(2012); 76 | }); 77 | 78 | it ('month', function() { 79 | var v = MidgardTest.TimeStamp; 80 | v.month = 6; 81 | v.month.should.be.a('number'); 82 | v.month.should.equal(6); 83 | }); 84 | 85 | it ('day', function() { 86 | var v = MidgardTest.TimeStamp; 87 | v.day = 7; 88 | v.day.should.be.a('number'); 89 | v.day.should.equal(7); 90 | }); 91 | 92 | it ('hour', function() { 93 | var v = MidgardTest.TimeStamp; 94 | v.hour = 14; 95 | v.hour.should.be.a('number'); 96 | v.hour.should.equal(14); 97 | }); 98 | 99 | it ('minute', function() { 100 | var v = MidgardTest.TimeStamp; 101 | v.minute = 35; 102 | v.minute.should.be.a('number'); 103 | v.minute.should.equal(35); 104 | }); 105 | 106 | it ('second', function() { 107 | var v = MidgardTest.TimeStamp; 108 | v.second = 0; 109 | v.second.should.be.a('number'); 110 | v.second.should.equal(0); 111 | }); 112 | 113 | it ('get string', function() { 114 | var v = MidgardTest.TimeStamp; 115 | v.get_string().should.be.a('string'); 116 | v.get_string().should.equal('2012-06-07 14:35:00+0000'); 117 | }); 118 | 119 | }); 120 | 121 | }); 122 | 123 | -------------------------------------------------------------------------------- /tests/midgard/test_401_boxed_timestamp.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var gir = require('../../gir'); 6 | gir.init(); 7 | 8 | var GObject = gir.load('GObject'); 9 | var Midgard = gir.load('Midgard'); 10 | Midgard.init(); 11 | 12 | var MidgardTest = require('./midgard_connection'); 13 | 14 | // Test G_TYPE_BOXED 15 | // metadata.created is C structure hold in GValue of boxed type 16 | 17 | describe('Midgard.TimeStamp (Boxed)', function() { 18 | 19 | describe('Get default values', function() { 20 | 21 | it ('year', function() { 22 | var v = MidgardTest.TestBook.metadata.created; 23 | v.year.should.be.a('number'); 24 | v.year.should.equal(1); 25 | }); 26 | 27 | it ('month', function() { 28 | var v = MidgardTest.TestBook.metadata.created; 29 | v.month.should.be.a('number'); 30 | v.month.should.equal(1); 31 | }); 32 | 33 | it ('day', function() { 34 | var v = MidgardTest.TestBook.metadata.created; 35 | v.day.should.be.a('number'); 36 | v.day.should.equal(1); 37 | }); 38 | 39 | it ('hour', function() { 40 | var v = MidgardTest.TestBook.metadata.created; 41 | v.hour.should.be.a('number'); 42 | v.hour.should.equal(0); 43 | }); 44 | 45 | it ('minute', function() { 46 | var v = MidgardTest.TestBook.metadata.created; 47 | v.minute.should.be.a('number'); 48 | v.minute.should.equal(0); 49 | }); 50 | 51 | it ('second', function() { 52 | var v = MidgardTest.TestBook.metadata.created; 53 | v.second.should.be.a('number'); 54 | v.second.should.equal(0); 55 | }); 56 | 57 | it ('get string', function() { 58 | var v = MidgardTest.TestBook.metadata.created; 59 | v.get_string().should.be.a('string'); 60 | v.get_string().should.equal('0001-01-01 00:00:00+0000'); 61 | }); 62 | }); 63 | 64 | describe('Set year', function() { 65 | 66 | it ('created', function() { 67 | var v = MidgardTest.TestBook.metadata.created; 68 | v.year = 2012; 69 | v.year.should.be.a('number'); 70 | v.year.should.equal(2012); 71 | }); 72 | 73 | it ('revised', function() { 74 | var v = MidgardTest.TestBook.metadata.revised; 75 | v.year = 2013; 76 | v.year.should.be.a('number'); 77 | v.year.should.equal(2013); 78 | }); 79 | 80 | it ('published', function() { 81 | var v = MidgardTest.TestBook.metadata.created; 82 | v.year = 2014; 83 | v.year.should.be.a('number'); 84 | v.year.should.equal(2014); 85 | }); 86 | 87 | }); 88 | 89 | describe('Set month', function() { 90 | 91 | it ('created', function() { 92 | var v = MidgardTest.TestBook.metadata.created; 93 | v.month = 1; 94 | v.month.should.be.a('number'); 95 | v.month.should.equal(1); 96 | }); 97 | 98 | it ('revised', function() { 99 | var v = MidgardTest.TestBook.metadata.revised; 100 | v.month = 2; 101 | v.month.should.be.a('number'); 102 | v.month.should.equal(2); 103 | }); 104 | 105 | it ('published', function() { 106 | var v = MidgardTest.TestBook.metadata.created; 107 | v.month = 3; 108 | v.month.should.be.a('number'); 109 | v.month.should.equal(3); 110 | }); 111 | 112 | }); 113 | 114 | describe('Set day', function() { 115 | 116 | it ('created', function() { 117 | var v = MidgardTest.TestBook.metadata.created; 118 | v.day = 1; 119 | v.day.should.be.a('number'); 120 | v.day.should.equal(1); 121 | }); 122 | 123 | it ('revised', function() { 124 | var v = MidgardTest.TestBook.metadata.revised; 125 | v.day = 2; 126 | v.day.should.be.a('number'); 127 | v.day.should.equal(2); 128 | }); 129 | 130 | it ('published', function() { 131 | var v = MidgardTest.TestBook.metadata.published; 132 | v.day = 3; 133 | v.day.should.be.a('number'); 134 | v.day.should.equal(3); 135 | }); 136 | }); 137 | 138 | describe('Set hour', function() { 139 | 140 | it ('created', function() { 141 | var v = MidgardTest.TestBook.metadata.created; 142 | v.hour = 1; 143 | v.hour.should.be.a('number'); 144 | v.hour.should.equal(1); 145 | }); 146 | 147 | it ('revised', function() { 148 | var v = MidgardTest.TestBook.metadata.revised; 149 | v.hour = 2; 150 | v.hour.should.be.a('number'); 151 | v.hour.should.equal(2); 152 | }); 153 | 154 | it ('published', function() { 155 | var v = MidgardTest.TestBook.metadata.published; 156 | v.hour = 3; 157 | v.hour.should.be.a('number'); 158 | v.hour.should.equal(3); 159 | }); 160 | }); 161 | 162 | describe('Set minute', function() { 163 | 164 | it ('created', function() { 165 | var v = MidgardTest.TestBook.metadata.created; 166 | v.minute = 1; 167 | v.minute.should.be.a('number'); 168 | v.minute.should.equal(1); 169 | }); 170 | 171 | it ('revised', function() { 172 | var v = MidgardTest.TestBook.metadata.revised; 173 | v.minute = 2; 174 | v.minute.should.be.a('number'); 175 | v.minute.should.equal(2); 176 | }); 177 | 178 | it ('published', function() { 179 | var v = MidgardTest.TestBook.metadata.published; 180 | v.minute = 3; 181 | v.minute.should.be.a('number'); 182 | v.minute.should.equal(3); 183 | }); 184 | }); 185 | 186 | describe('Set second', function() { 187 | 188 | it ('created', function() { 189 | var v = MidgardTest.TestBook.metadata.created; 190 | v.second = 1; 191 | v.second.should.be.a('number'); 192 | v.second.should.equal(1); 193 | }); 194 | 195 | it ('revised', function() { 196 | var v = MidgardTest.TestBook.metadata.revised; 197 | v.second = 2; 198 | v.second.should.be.a('number'); 199 | v.second.should.equal(2); 200 | }); 201 | 202 | it ('published', function() { 203 | var v = MidgardTest.TestBook.metadata.published; 204 | v.second = 3; 205 | v.second.should.be.a('number'); 206 | v.second.should.equal(3); 207 | }); 208 | }); 209 | 210 | describe('Get string', function() { 211 | 212 | it ('created', function() { 213 | var v = MidgardTest.TestBook.metadata.created; 214 | v.year = 2012; 215 | v.month = 6; 216 | v.day = 7; 217 | v.hour = 14; 218 | v.minute = 35; 219 | v.second = 0; 220 | v.get_string().should.be.a('string'); 221 | v.get_string().should.equal('2012-06-07 14:35:00+0000'); 222 | }); 223 | }); 224 | }); 225 | -------------------------------------------------------------------------------- /tests/midgard/test_500_object_metadata.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var gir = require('../../gir'); 6 | gir.init(); 7 | 8 | var GObject = gir.load('GObject'); 9 | var Midgard = gir.load('Midgard'); 10 | Midgard.init(); 11 | 12 | var MidgardTest = require('./midgard_connection'); 13 | 14 | var mgd = MidgardTest.cnc; 15 | var TestBook = Midgard.Object.factory(mgd, "gir_test_book_store"); 16 | 17 | var mdata = TestBook.metadata; 18 | mdata.score = -1; 19 | mdata.authors = "Sir Lancelot and Knights"; 20 | mdata.hidden = false; 21 | var published = mdata.published; 22 | published.year = 2012; 23 | published.month = 7; 24 | published.day = 1; 25 | var datestring = "2012-07-01 00:00:00+0000"; 26 | 27 | describe('Midgard.Metadata', function() { 28 | 29 | describe('Create', function() { 30 | 31 | it('is created', function () { 32 | TestBook.metadata.published = published; 33 | var created = TestBook.create(); 34 | MidgardTest.cnc.get_error_string().should.equal("MGD_ERR_OK"); 35 | MidgardTest.cnc.get_error().should.equal(Midgard.GenericError.ok); 36 | created.should.equal(true); 37 | TestBook.metadata.should.be.a('object'); 38 | }); 39 | 40 | it('is metadata', function () { 41 | TestBook.metadata.should.be.a('object'); 42 | }); 43 | 44 | it('test score', function () { 45 | TestBook.metadata.score.should.be.a('number'); 46 | TestBook.metadata.score.should.equal(-1); 47 | }); 48 | 49 | it('test authors', function () { 50 | TestBook.metadata.authors.should.be.a('string'); 51 | TestBook.metadata.authors.should.equal('Sir Lancelot and Knights'); 52 | }); 53 | 54 | it('test hidden', function () { 55 | TestBook.metadata.hidden.should.be.a('boolean'); 56 | TestBook.metadata.hidden.should.equal(false); 57 | }); 58 | 59 | it('test created', function () { 60 | var year = TestBook.metadata.created.year; 61 | var d = new Date(); 62 | year.should.be.a('number'); 63 | year.should.equal(d.getFullYear()); 64 | }); 65 | 66 | it('test creator', function () { 67 | var creator = TestBook.metadata.creator; 68 | creator.should.be.a('string'); 69 | creator.should.equal(''); 70 | }); 71 | 72 | it('test revised', function () { 73 | var year = TestBook.metadata.revised.year; 74 | var d = new Date(); 75 | year.should.be.a('number'); 76 | year.should.equal(d.getFullYear()); 77 | }); 78 | 79 | it('test revisor', function () { 80 | var revisor = TestBook.metadata.revisor; 81 | revisor.should.be.a('string'); 82 | revisor.should.equal(''); 83 | }); 84 | 85 | it('test published', function () { 86 | var year = TestBook.metadata.published.year; 87 | var month = TestBook.metadata.published.month; 88 | var day = TestBook.metadata.published.day; 89 | year.should.be.a('number'); 90 | year.should.equal(2012); 91 | month.should.be.a('number'); 92 | month.should.equal(7); 93 | day.should.be.a('number'); 94 | day.should.equal(1); 95 | TestBook.metadata.published.get_string().should.equal(datestring); 96 | }); 97 | }); 98 | 99 | describe('Update', function() { 100 | 101 | it('is updated', function () { 102 | var mdata = TestBook.metadata; 103 | mdata.score = 1; 104 | mdata.authors = "Sir Lancelot and Knights and King Arthur"; 105 | mdata.hidden = true; 106 | TestBook.update(); 107 | MidgardTest.cnc.get_error_string().should.equal("MGD_ERR_OK"); 108 | TestBook.metadata.should.be.a('object'); 109 | }); 110 | 111 | it('test score', function () { 112 | TestBook.metadata.score.should.equal(1); 113 | }); 114 | 115 | it('test authors', function () { 116 | TestBook.metadata.authors.should.equal('Sir Lancelot and Knights and King Arthur'); 117 | }); 118 | 119 | it('test hidden', function () { 120 | TestBook.metadata.hidden.should.equal(true); 121 | }); 122 | 123 | it('test revised', function () { 124 | var revised = TestBook.metadata.revised; 125 | var d = new Date(); 126 | revised.year.should.equal(d.getFullYear()); 127 | revised.month.should.equal(d.getMonth() + 1); // Data,getMonth() range is 0-11 128 | revised.day.should.equal(d.getDate()); 129 | }); 130 | 131 | it('test creator', function () { 132 | var creator = TestBook.metadata.creator; 133 | creator.should.equal(''); 134 | }); 135 | 136 | }); 137 | 138 | describe('Delete', function() { 139 | 140 | it('is deleted', function () { 141 | var deleted = TestBook.delete(false); 142 | MidgardTest.cnc.get_error_string().should.equal('MGD_ERR_OK'); 143 | deleted.should.equal(true); 144 | }); 145 | }); 146 | describe('Purge', function() { 147 | 148 | it('is purged', function () { 149 | var purged = TestBook.purge(false); 150 | MidgardTest.cnc.get_error_string().should.equal('MGD_ERR_OK'); 151 | purged.should.equal(true); 152 | TestBook.metadata.deleted.should.equal(true); 153 | }); 154 | }); 155 | }); 156 | -------------------------------------------------------------------------------- /tests/midgard/test_510_object_crud.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var gir = require('../../gir'); 6 | gir.init(); 7 | 8 | var GObject = gir.load('GObject'); 9 | var Midgard = gir.load('Midgard'); 10 | Midgard.init(); 11 | 12 | var MidgardTest = require('./midgard_connection'); 13 | 14 | var book = MidgardTest.TestBook; 15 | var bookstore = MidgardTest.TestBookStore; 16 | 17 | describe('Midgard.Object CRUD', function() { 18 | 19 | describe('Create', function() { 20 | 21 | var book = MidgardTest.TestBook; 22 | 23 | it('is created bookstore', function() { 24 | var created = bookstore.create(); 25 | MidgardTest.cnc.get_error_string().should.equal('MGD_ERR_OK'); 26 | MidgardTest.cnc.get_error().should.equal(Midgard.GenericError.ok); 27 | created.should.be.a('boolean'); 28 | created.should.equal(true); 29 | }); 30 | 31 | it('is created book', function() { 32 | book.store = bookstore.id; 33 | var created = book.create(); 34 | MidgardTest.cnc.get_error_string().should.equal('MGD_ERR_OK'); 35 | MidgardTest.cnc.get_error().should.equal(Midgard.GenericError.ok); 36 | created.should.be.a('boolean'); 37 | created.should.equal(true); 38 | }); 39 | 40 | it('check error code', function() { 41 | var error_code = MidgardTest.cnc.get_error(); 42 | error_code.should.equal(Midgard.GenericError.ok); 43 | }); 44 | 45 | it('check error string', function() { 46 | var error_string = MidgardTest.cnc.get_error_string(); 47 | error_string.should.equal("MGD_ERR_OK"); 48 | }); 49 | 50 | it('is valid guid', function() { 51 | var valid_guid = true; 52 | valid_guid.should.equal(Midgard.is_guid(book.guid)); 53 | }); 54 | 55 | }); 56 | 57 | describe('Update', function() { 58 | 59 | var book = MidgardTest.TestBook; 60 | book.title = 'The Holy Grail 2'; 61 | 62 | it('is updated', function() { 63 | var updated = book.update(); 64 | updated.should.be.a('boolean'); 65 | updated.should.equal(true); 66 | }); 67 | 68 | it('check error code', function() { 69 | var error_code = MidgardTest.cnc.get_error(); 70 | error_code.should.equal(Midgard.GenericError.ok); 71 | }); 72 | 73 | it('check error string', function() { 74 | var error_string = MidgardTest.cnc.get_error_string(); 75 | error_string.should.equal("MGD_ERR_OK"); 76 | }); 77 | 78 | it('is valid guid', function() { 79 | var valid_guid = true; 80 | valid_guid.should.equal(Midgard.is_guid(book.guid)); 81 | }); 82 | }); 83 | 84 | describe('Delete', function() { 85 | 86 | var book = MidgardTest.TestBook; 87 | 88 | it('is deleted', function() { 89 | var deleted = book.delete(false); 90 | deleted.should.be.a('boolean'); 91 | deleted.should.equal(true); 92 | }); 93 | 94 | it('check error code', function() { 95 | var error_code = MidgardTest.cnc.get_error(); 96 | error_code.should.equal(Midgard.GenericError.ok); 97 | }); 98 | 99 | it('check error string', function() { 100 | var error_string = MidgardTest.cnc.get_error_string(); 101 | error_string.should.equal("MGD_ERR_OK"); 102 | }); 103 | }); 104 | 105 | describe('Purge book', function() { 106 | 107 | var book = MidgardTest.TestBook; 108 | 109 | it('is purged', function() { 110 | var purged = book.purge(false); 111 | purged.should.be.a('boolean'); 112 | purged.should.equal(true); 113 | }); 114 | 115 | it('check error code', function() { 116 | var error_code = MidgardTest.cnc.get_error(); 117 | error_code.should.equal(Midgard.GenericError.ok); 118 | }); 119 | 120 | it('check error string', function() { 121 | var error_string = MidgardTest.cnc.get_error_string(); 122 | error_string.should.equal("MGD_ERR_OK"); 123 | }); 124 | }); 125 | 126 | describe('Purge bookstore', function() { 127 | 128 | it('is purged', function() { 129 | var purged = bookstore.purge(false); 130 | purged.should.be.a('boolean'); 131 | purged.should.equal(true); 132 | }); 133 | 134 | it('check error code', function() { 135 | var error_code = MidgardTest.cnc.get_error(); 136 | error_code.should.equal(Midgard.GenericError.ok); 137 | }); 138 | 139 | it('check error string', function() { 140 | var error_string = MidgardTest.cnc.get_error_string(); 141 | error_string.should.equal("MGD_ERR_OK"); 142 | }); 143 | }); 144 | 145 | }); 146 | -------------------------------------------------------------------------------- /tests/midgard/test_550_signals.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var gir = require('../../gir'); 6 | gir.init(); 7 | 8 | var GObject = gir.load('GObject'); 9 | var Midgard = gir.load('Midgard'); 10 | Midgard.init(); 11 | 12 | var config = new Midgard.Config(); 13 | config.read_file_at_path(__dirname + '/test_SQLITE.conf'); 14 | 15 | var MidgardTest = require('./midgard_connection'); 16 | 17 | var mgd = MidgardTest.cnc 18 | var from_connection_callback = ''; 19 | var from_object_callback = ''; 20 | var from_object_update_callback = ''; 21 | var obj = null; 22 | 23 | // Callbacks 24 | mgd.on('error', function() { 25 | from_connection_callback = mgd.get_error_string(); 26 | }); 27 | 28 | // Tests 29 | 30 | describe('Signals', function() { 31 | 32 | describe('Midgard.Connection', function() { 33 | 34 | it('create object error', function() { 35 | obj = Midgard.Object.factory(mgd, "gir_test_book_store"); 36 | obj.create(); 37 | from_connection_callback.should.equal('MGD_ERR_OK'); 38 | }); 39 | 40 | it('purge object error', function() { 41 | obj.purge(false); 42 | from_connection_callback.should.equal('MGD_ERR_OK'); 43 | }); 44 | 45 | it('invalid signal name', function() { 46 | try { 47 | win.on('invalid-name', function() { }); 48 | } catch (err) { 49 | // do nothing 50 | } 51 | }); 52 | 53 | }); 54 | 55 | describe('Midgard.Object', function() { 56 | 57 | it('action-create', function() { 58 | obj = Midgard.Object.factory(mgd, "gir_test_book_store"); 59 | obj.on('action-create', function() { 60 | from_object_callback = 'Object create'; 61 | }); 62 | obj.create(); 63 | from_object_callback.should.equal('Object create'); 64 | from_connection_callback.should.equal('MGD_ERR_OK'); 65 | }); 66 | 67 | it('action-update', function() { 68 | //obj = Midgard.Object.factory(mgd, "gir_test_book_store"); 69 | obj.on('action-update', function() { 70 | from_object_update_callback = 'Object update'; 71 | }); 72 | obj.update(); 73 | from_object_update_callback.should.equal('Object update'); 74 | from_object_update_callback = ''; 75 | // Check if callback is still alive 76 | obj.update(); 77 | from_object_update_callback.should.equal('Object update'); 78 | }); 79 | 80 | it('action-purge', function() { 81 | //obj = Midgard.Object.factory(mgd, "gir_test_book_store"); 82 | obj.on('action-purge', function() { 83 | from_object_callback = 'Object purge'; 84 | }); 85 | obj.purge('false'); 86 | from_object_callback.should.equal('Object purge'); 87 | from_connection_callback.should.equal('MGD_ERR_OK'); 88 | }); 89 | 90 | it('invalid signal name', function() { 91 | try { 92 | obj.on('invalid-name', function() { }); 93 | } catch (err) { 94 | // do nothing 95 | } 96 | try { 97 | obj.on('invalid_name', function() { }); 98 | } catch (err) { 99 | // do nothing 100 | } 101 | }); 102 | }); 103 | }); 104 | -------------------------------------------------------------------------------- /tests/midgard/test_600_user.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var gir = require('../../gir'); 6 | gir.init(); 7 | 8 | var GObject = gir.load('GObject'); 9 | var Midgard = gir.load('Midgard'); 10 | Midgard.init(); 11 | 12 | var MidgardTest = require('./midgard_connection'); 13 | 14 | describe('Midgard.User', function() { 15 | 16 | describe('Instance', function() { 17 | it('is instance', function() { 18 | var user = new Midgard.User({'connection':MidgardTest.cnc, 'login':'John', 'authtype':'Plaintext', 'active':true}); 19 | user.should.be.a('object'); 20 | }); 21 | }); 22 | 23 | describe('John', function() { 24 | 25 | it('is created', function () { 26 | var user = new Midgard.User({'connection':MidgardTest.cnc, 'login':'John', 'authtype':'Plaintext', 'active':true}); 27 | var created = user.create(); 28 | MidgardTest.cnc.get_error_string().should.equal('MGD_ERR_OK'); 29 | MidgardTest.cnc.get_error().should.equal(Midgard.GenericError.ok); 30 | created.should.equal(true); 31 | }); 32 | 33 | it('is not created, duplicated', function () { 34 | var user = new Midgard.User({'connection':MidgardTest.cnc, 'login':'John', 'authtype':'Plaintext', 'active':true}); 35 | var created = user.create(); 36 | MidgardTest.cnc.get_error_string().should.equal('Object already exist.'); 37 | MidgardTest.cnc.get_error().should.equal(Midgard.GenericError.duplicate); 38 | created.should.equal(false); 39 | }); 40 | }); 41 | 42 | describe('Alice', function() { 43 | 44 | it('is updated', function () { 45 | var user = new Midgard.User({'connection':MidgardTest.cnc, 'login':'Alice', 'authtype':'Plaintext', 'active':true}); 46 | var created = user.create(); 47 | MidgardTest.cnc.get_error_string().should.equal('MGD_ERR_OK'); 48 | MidgardTest.cnc.get_error().should.equal(Midgard.GenericError.ok); 49 | created.should.equal(true) 50 | user.active = false; 51 | var updated = user.update(); 52 | MidgardTest.cnc.get_error_string().should.equal('MGD_ERR_OK'); 53 | MidgardTest.cnc.get_error().should.equal(Midgard.GenericError.ok); 54 | updated.should.equal(true); 55 | }); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /tests/midgard/test_SQLITE.conf: -------------------------------------------------------------------------------- 1 | # This is example configuration file 2 | # Do not create config files with conf extension 3 | 4 | [MidgardDir] 5 | 6 | # Shared files, architecture independent 7 | # Default is $prefix/share/midgard 8 | ShareDir=tests/midgard/test_data 9 | 10 | # Application specific files 11 | # Default is /var(/local)/lib/midgard 12 | #VarDir= 13 | 14 | # Directory to store binary data 15 | # Default is /var(/local)/lib/midgard/blobs/DBNAME 16 | BlobDir=tests/midgard/test_data/blobs 17 | 18 | # Directory for cached data 19 | # Default is /var/cache/midgard 20 | #CacheDir= 21 | 22 | [MidgardDatabase] 23 | 24 | #Database type, by default MySQL 25 | #Values: MySQL, PostgreSQL, FreeTDS, ODBC, SQLite 26 | Type=SQLite 27 | 28 | #Database host ( 'localhost' by default ) 29 | Host= 30 | 31 | #Database port (0 by default) 32 | #Port=3306 33 | 34 | #Name of the database 35 | Name=test_gir 36 | 37 | #Username for user who is able to connect to database 38 | Username=midgard 39 | 40 | #Password for username 41 | Password=midgard 42 | 43 | #Directory for SQLite database file ('~/.midgard2/data' by default) 44 | DatabaseDir=tests/midgard/test_data 45 | 46 | #Default language used by application. Unknown by default. 47 | #DefaultLanguage=pl 48 | 49 | #Full path to logfile. In none is defined output will be printed to stdout. 50 | #There's no default value for log file. 51 | #Logfile=/var/log/midgard/midgard.log 52 | 53 | #Log level. 54 | #Possible values: error, critical, warning (warn), message, info, debug 55 | #Default level is 'warn'. 56 | Loglevel=warn 57 | 58 | #Create tables and columns. Boolean value. Default is false 59 | #TableCreate=true 60 | 61 | #Update tables and columns. Set to true if you want to modify already 62 | #existing tables and columns. Boolean value. Default is false. 63 | #TableUpdate=true 64 | 65 | #You shouldn't use configuration below in real life 66 | 67 | #Testunit for all types defined in schema. Boolean value. Default is false. 68 | #TestUnit=true 69 | 70 | #Midgard person's username. Login delimeters with sitegroups may be used as well. 71 | #No default value. 72 | #MidgardUsername=root 73 | 74 | #Midgard person's password 75 | #No default value 76 | #MidgardPassword=password 77 | 78 | #Midgard authentication Type 79 | #Values: Normal, PAM, Trusted 80 | #Default value is Normal auth type 81 | #AuthType=PAM 82 | 83 | #Name of the file used by PAM authentication type 84 | #Default value is 'midgard'. 85 | #PamFile=mypamfile 86 | 87 | #Disable or Enable GdaThreads 88 | #Default value is false 89 | #GdaThreads=true 90 | -------------------------------------------------------------------------------- /tests/midgard/test_data/MidgardObjects.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | http://xmlns.com/foaf/0.1/Person 6 | foaf:http://xmlns.com/foaf/0.1/ 7 | 8 | 9 | Local non-replication-safe database identifier 10 | 11 | 12 | First name of the person 13 | foaf:firstName 14 | 15 | 16 | Last name of the person 17 | foaf:lastName 18 | 19 | 20 | 21 | 22 | 23 | Local non-replication-safe database identifier 24 | 25 | 26 | Filename of the attachment 27 | 28 | 29 | Title of the attachment 30 | 31 | 32 | Location of the attachment in the blob directory structure 33 | 34 | 35 | MIME type of the attachment 36 | 37 | 38 | GUID of the object the attachment is attached to 39 | 40 | 41 | 42 | 43 | 44 | Local non-replication-safe database identifier 45 | 46 | 47 | Namespace of the parameter 48 | 49 | 50 | Key of the parameter 51 | 52 | 53 | Value of the parameter 54 | 55 | 56 | GUID of the object the parameter extends 57 | 58 | 59 | 60 | 61 | 62 | Local non-replication-safe database identifier 63 | 64 | 65 | Path name of the snippetdir 66 | 67 | 68 | Snippetdir the snippetdir is under 69 | 70 | 71 | 72 | 73 | 74 | Local non-replication-safe database identifier 75 | 76 | 77 | Path name of the snippet 78 | 79 | 80 | Snippetdir the snippet is under 81 | 82 | 83 | Code of the snippet 84 | 85 | 86 | Documentation of the snippet 87 | 88 | 89 | 90 | 91 | 92 | Local non-replication-safe database identifier 93 | 94 | 95 | Quota for the sitegroup (in bytes) 96 | 97 | 98 | Disk usage of the sitegroup (in bytes) 99 | 100 | 101 | Limit of number of records for the sitegroup 102 | 103 | 104 | Number of records for the sitegroup 105 | 106 | 107 | MgdSchema type the quota applies to 108 | 109 | 110 | Quota of the type for the sitegroup (in bytes) 111 | 112 | 113 | Disk usage of the type of the sitegroup (in bytes) 114 | 115 | 116 | Limit of number of records of the type for the sitegroup 117 | 118 | 119 | Number of records of the type for the sitegroup 120 | 121 | 122 | 123 | 124 | 125 | http://xmlns.notu.be/aair#Activity 126 | aair:http://xmlns.notu.be/aair# 127 | 128 | 129 | Local non-replication-safe database identifier 130 | 131 | 132 | The person who performed the activity 133 | aair:Actor 134 | 135 | 136 | 137 | The action performed, following Atom Activity Extension URL schema (for example: http://activitystrea.ms/schema/1.0/post) 138 | aair:activityVerb 139 | 140 | 141 | The object that the action was done to 142 | aair:activityObject 143 | 144 | 145 | A human-readable description of the activity 146 | aair:summary 147 | 148 | 149 | Application the activity was performed with. In case of MidCOM, a component 150 | aair:Application 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /tests/midgard/test_data/midgard_auth_types.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 1 7 | 8 | 9 | 10 | 11 | 2 12 | 13 | 14 | 15 | 16 | 3 17 | 18 | 19 | 20 | 21 | 4 22 | 23 | 24 | 25 | 26 | 5 27 | 28 | 29 | 30 | 31 | 6 32 | 33 | 34 | 35 | 36 | 7 37 | 38 | 39 | 40 | 41 | 8 42 | 43 | 44 | 45 | 46 | 9 47 | 48 | 49 | 50 | 51 | 10 52 | 53 | 54 | 55 | 56 | 11 57 | 58 | 59 | 60 | 61 | 12 62 | 63 | 64 | 65 | 66 | 13 67 | 68 | 69 | 70 | 71 | 14 72 | 73 | 74 | 75 | 76 | 15 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /tests/midgard/test_data/schema/phpcr_mixin_schemas.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | STRING 9 | false 10 | false 11 | false 12 | false 13 | 14 | 15 | STRING 16 | false 17 | false 18 | false 19 | false 20 | 21 | 22 | 23 | This mixin node type can be used to add standardized title and description properties to a node. 24 | 25 | true 26 | 27 | 28 | 29 | false 30 | false 31 | false 32 | false 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | DATE 41 | true 42 | false 43 | true 44 | false 45 | 46 | 47 | STRING 48 | true 49 | false 50 | true 51 | false 52 | 53 | 54 | 55 | This mixin node type can be used to add standardized creation information 56 | properties to a node. In implementations that make these properties protected, 57 | their values are controlled by the repository, which should set them appropriately 58 | upon the initial persist of a node with this mixin type. In cases where this mixin is 59 | added to an already existing node the semantics of these properties are 60 | implementation specific (see §10.10.3 Assigning Mixin Node Types). 61 | 62 | 63 | true 64 | 65 | 66 | 67 | false 68 | false 69 | false 70 | false 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | DATE 79 | true 80 | false 81 | false 82 | false 83 | 84 | 85 | STRING 86 | true 87 | false 88 | false 89 | false 90 | 91 | 92 | 93 | This mixin node type can be used to provide standardized modification 94 | information properties to a node. In implementations that make these properties 95 | protected, their values are controlled by the repository, which should set them 96 | appropriately upon a significant modification of the subgraph of a node with this 97 | mixin. What constitutes a significant modification will depend on the semantics of 98 | the various parts of a node's subgraph and is implementation-dependent. 99 | 100 | 101 | true 102 | 103 | 104 | 105 | false 106 | false 107 | false 108 | false 109 | mix:lastModified 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | STRING 118 | false 119 | false 120 | false 121 | false 122 | 123 | 124 | 125 | This mixin node type can be used to provide a standardized property that 126 | specifies the natural language in which the content of a node is expressed. The 127 | value of the jcr:language property should be a language code as defined in RFC 128 | 129 | 130 | true 131 | 132 | 133 | 134 | false 135 | false 136 | false 137 | false 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | STRING 146 | false 147 | false 148 | false 149 | false 150 | 151 | 152 | STRING 153 | false 154 | false 155 | false 156 | false 157 | 158 | 159 | 160 | This mixin node type can be used to provide standardized mimetype and 161 | encoding properties to a node. 162 | If a node of this type has a primary item that is a single-value BINARY property 163 | then jcr:mimeType property indicates the media type6 applicable to the contents 164 | of that property and, if that media type is one to which a text encoding applies, 165 | the jcr:encoding property indicates the character set7 used. 166 | If a node of this type does not meet the above precondition then the 167 | interpretation of the jcr:mimeType and jcr:encoding properties is 168 | implementation-dependent. 169 | 170 | 171 | true 172 | 173 | 174 | 175 | false 176 | false 177 | false 178 | false 179 | mix:mimeType 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | STRING 188 | true 189 | false 190 | true 191 | false 192 | 193 | 194 | 195 | A jcr:etag property is an opaque string whose syntax is identical to that defined 196 | for entity tags in HTTP/1.1. Semantically, the jcr:etag is comparable to the 197 | HTTP/1.1 strong entity tag. 198 | 199 | 200 | true 201 | 202 | 203 | 204 | false 205 | false 206 | false 207 | false 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | STRING 216 | true 217 | true 218 | true 219 | false 220 | 221 | 222 | 223 | This node type adds an auto-created, mandatory, protected STRING property to 224 | the node, called jcr:uuid, which exposes the identifier of the node. Note that the 225 | term “UUID” is used for backward compatibility with JCR 1.0 and does not 226 | necessarily imply the use of the UUID syntax, or global uniqueness. 227 | The identifier of a referenceable node must be a referenceable identifier. 228 | Referenceable identifiers must fulfill a number of constraints beyond the 229 | minimum required of standard identifiers (see §3.8.3 Referenceable Identifiers). 230 | 231 | 232 | true 233 | 234 | 235 | 236 | false 237 | false 238 | false 239 | false 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | All shareable nodes are referenceable. 249 | mix:referenceable 250 | true 251 | 252 | 253 | 254 | false 255 | false 256 | false 257 | false 258 | 259 | 260 | 261 | 262 | -------------------------------------------------------------------------------- /tests/midgard/test_data/schema/phpcr_schemas.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | The type of a node 9 | COMPUTE 10 | NAME 11 | true 12 | true 13 | true 14 | false 15 | 16 | 17 | Mixin types multivalue 18 | NAME 19 | COMPUTE 20 | false 21 | false 22 | true 23 | true 24 | 25 | 26 | 27 | 28 | 29 | false 30 | 31 | 32 | 33 | false 34 | false 35 | 36 | false 37 | false 38 | true 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | mix:created 47 | false 48 | 49 | 50 | 51 | false 52 | false 53 | 54 | false 55 | false 56 | true 57 | 58 | 59 | 60 | 61 | 62 | 63 | Local non-replication-safe database identifier 64 | 65 | 66 | 67 | 68 | nt:hierarchyNode 69 | false 70 | 71 | * 72 | nt:hierarchyNode 73 | 74 | false 75 | false 76 | VERSION 77 | false 78 | false 79 | 80 | 81 | 82 | 83 | 84 | 85 | Local non-replication-safe database identifier 86 | 87 | 88 | 89 | 90 | nt:hierarchyNode 91 | false 92 | false 93 | jcr:content 94 | jcr:content 95 | nt:base 96 | 97 | false 98 | true 99 | COPY 100 | false 101 | false 102 | 103 | 104 | 105 | 106 | 107 | 108 | Local non-replication-safe database identifier 109 | 110 | 111 | 112 | The type of a node 113 | REFERENCE 114 | 115 | false 116 | true 117 | false 118 | false 119 | 120 | 121 | 122 | 123 | nt:hierarchyNode 124 | false 125 | false 126 | jcr:content 127 | nt:linkedFile 128 | 129 | 130 | 131 | 132 | 133 | 134 | Local non-replication-safe database identifier 135 | 136 | 137 | 138 | 139 | 140 | 141 | nt:base 142 | false 143 | true 144 | 145 | * 146 | nt:base 147 | nt:unstructured 148 | false 149 | false 150 | VERSION 151 | false 152 | true 153 | 154 | 155 | 156 | 157 | 158 | 159 | Local non-replication-safe database identifier 160 | 161 | 162 | 163 | Binary data 164 | BINARY 165 | COPY 166 | false 167 | true 168 | false 169 | false 170 | 171 | 172 | 173 | 174 | 175 | 176 | nt:base mix:referenceable mix:mimeType mix:lastModified 177 | false 178 | false 179 | jcr:data 180 | nt:hierarchyNode 181 | 182 | false 183 | false 184 | VERSION 185 | false 186 | false 187 | 188 | 189 | 190 | 191 | 192 | 193 | Local non-replication-safe database identifier 194 | 195 | 196 | Query statement 197 | STRING 198 | COPY 199 | false 200 | false 201 | false 202 | false 203 | 204 | 205 | 206 | 207 | Query language 208 | STRING 209 | COPY 210 | false 211 | false 212 | false 213 | false 214 | 215 | 216 | 217 | 218 | 219 | 220 | This node type may be used to store a persistent query. 221 | nt:base 222 | false 223 | false 224 | 225 | 226 | 227 | false 228 | false 229 | VERSION 230 | false 231 | false 232 | 233 | 234 | 235 | 236 | 237 | 238 | Local non-replication-safe database identifier 239 | 240 | 241 | 242 | 243 | 244 | 245 | STRING 246 | 247 | 248 | 249 | 250 | nt:address 251 | false 252 | 253 | 254 | 255 | false 256 | false 257 | VERSION 258 | false 259 | false 260 | 261 | 262 | 263 | 264 | -------------------------------------------------------------------------------- /tests/midgard/test_data/schema/test_book_crud.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/midgard/test_query_100_column.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var gir = require('../../gir'); 6 | gir.init(); 7 | 8 | var GObject = gir.load('GObject'); 9 | var Midgard = gir.load('Midgard'); 10 | Midgard.init(); 11 | 12 | var MidgardTest = require('./midgard_connection'); 13 | var column; 14 | var property; 15 | 16 | describe('Midgard.SqlQueryColumn', function() { 17 | 18 | it('is instance', function() { 19 | property = new Midgard.QueryProperty({'property':'title'}) 20 | column = new Midgard.SqlQueryColumn({'queryproperty':property, 'name':'The title', 'qualifier':'t1'}); 21 | column.should.be.a('object'); 22 | //column.should.be.an.instanceof(Midgard.SqlQueryColumn); 23 | }); 24 | 25 | it('has queryproperty', function() { 26 | var prop = column.get_query_property(); 27 | //prop.should.equal(prop); 28 | prop.property.should.equal(property.property); 29 | }); 30 | 31 | it('has name', function() { 32 | var name = column.get_name(); 33 | name.should.equal('The title'); 34 | }); 35 | 36 | it('has qualifier', function() { 37 | var q = column.get_qualifier(); 38 | q.should.equal('t1'); 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /tests/midgard/test_query_200_sql_query_select_data.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var gir = require('../../gir'); 6 | gir.init(); 7 | 8 | var GObject = gir.load('GObject'); 9 | var Midgard = gir.load('Midgard'); 10 | Midgard.init(); 11 | 12 | var MidgardTest = require('./midgard_connection'); 13 | var select; 14 | var property; 15 | 16 | describe('Midgard.SqlQuerySelectData', function() { 17 | 18 | it('add column', function() { 19 | select = new Midgard.SqlQuerySelectData({'connection':MidgardTest.cnc}); 20 | var column = new Midgard.SqlQueryColumn({ 21 | 'queryproperty': new Midgard.QueryProperty({'property':'a'}), 22 | 'name':'a', 23 | 'qualifier':'t1' 24 | }); 25 | //Expect exception in case of error 26 | select.add_column(column) 27 | }); 28 | 29 | it('get columns', function() { 30 | var column = new Midgard.SqlQueryColumn({ 31 | 'queryproperty': new Midgard.QueryProperty({'property':'b'}), 32 | 'name':'b', 33 | 'qualifier':'t2' 34 | }); 35 | select.add_column(column); 36 | 37 | var column = new Midgard.SqlQueryColumn({ 38 | 'queryproperty': new Midgard.QueryProperty({'property':'c'}), 39 | 'name':'c', 40 | 'qualifier':'t3' 41 | }); 42 | select.add_column(column); 43 | 44 | var columns = select.get_columns(); 45 | columns.length.should.equal(3); 46 | columns[0].get_name().should.equal('a'); 47 | columns[0].get_qualifier().should.equal('t1'); 48 | columns[1].get_name().should.equal('b'); 49 | columns[1].get_qualifier().should.equal('t2'); 50 | columns[2].get_name().should.equal('c'); 51 | columns[2].get_qualifier().should.equal('t3'); 52 | }); 53 | 54 | it('execute', function() { 55 | var sel = new Midgard.SqlQuerySelectData({'connection':MidgardTest.cnc}); 56 | var storage = new Midgard.QueryStorage({'dbclass':'midgard_person'}); 57 | var column = new Midgard.SqlQueryColumn({ 58 | 'queryproperty': new Midgard.QueryProperty({'property':'firstname', 'storage':storage}), 59 | 'name':'name', 60 | 'qualifier':'p' 61 | }); 62 | sel.add_column(column); 63 | sel.execute(); 64 | }); 65 | 66 | it('get query result', function() { 67 | var sel = new Midgard.SqlQuerySelectData({'connection':MidgardTest.cnc}); 68 | var storage = new Midgard.QueryStorage({'dbclass':'gir_test_book_crud'}); 69 | var column = new Midgard.SqlQueryColumn({ 70 | 'queryproperty': new Midgard.QueryProperty({'property':'title', 'storage':storage}), 71 | 'name':'name', 72 | 'qualifier':'p' 73 | }); 74 | sel.add_column(column); 75 | sel.execute(); 76 | var result = sel.get_query_result(); 77 | result.should.be.a('object'); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /tests/midgard/test_query_300_sql_query_result.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var gir = require('../../gir'); 6 | gir.init(); 7 | 8 | var GObject = gir.load('GObject'); 9 | var Midgard = gir.load('Midgard'); 10 | Midgard.init(); 11 | 12 | var MidgardTest = require('./midgard_connection'); 13 | var select; 14 | var result; 15 | 16 | describe('Midgard.SqlQueryResult', function() { 17 | 18 | it('execute', function() { 19 | select = new Midgard.SqlQuerySelectData({'connection':MidgardTest.cnc}); 20 | var storage = new Midgard.QueryStorage({'dbclass':'midgard_person'}); 21 | var column = new Midgard.SqlQueryColumn({ 22 | 'queryproperty': new Midgard.QueryProperty({'property':'firstname', 'storage':storage}), 23 | 'name':'name', 24 | 'qualifier':'p' 25 | }); 26 | select.add_column(column); 27 | select.execute(); 28 | }); 29 | 30 | it('get rows', function() { 31 | result = select.get_query_result(); 32 | result.should.be.a('object'); 33 | var rows = result.get_rows(); 34 | Array.isArray(rows).should.equal(true); 35 | rows.length.should.equal(1); 36 | }); 37 | 38 | it('get columns', function() { 39 | result = select.get_query_result(); 40 | result.should.be.a('object'); 41 | var columns = result.get_columns(); 42 | Array.isArray(columns).should.equal(true); 43 | columns.length.should.equal(1); 44 | }); 45 | 46 | it('get columns qualifier', function() { 47 | result = select.get_query_result(); 48 | result.should.be.a('object'); 49 | var columns = result.get_columns(); 50 | columns.length.should.equal(1); 51 | var i = 0; 52 | for (i in columns) { 53 | columns[i].get_qualifier().should.equal('p'); 54 | } 55 | }); 56 | 57 | it('get columns name', function() { 58 | result = select.get_query_result(); 59 | result.should.be.a('object'); 60 | var columns = result.get_columns(); 61 | columns.length.should.equal(1); 62 | columns[0].get_name().should.equal('name'); 63 | }); 64 | 65 | it('get columns property name', function() { 66 | result = select.get_query_result(); 67 | result.should.be.a('object'); 68 | var columns = result.get_columns(); 69 | columns.length.should.equal(1); 70 | var query_prop = columns[0].get_query_property(); 71 | query_prop['property'].should.equal('firstname'); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /tests/midgard/test_query_400_sql_query_result_constraints.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var gir = require('../../gir'); 6 | gir.init(); 7 | 8 | var GObject = gir.load('GObject'); 9 | var Midgard = gir.load('Midgard'); 10 | Midgard.init(); 11 | 12 | var MidgardTest = require('./midgard_connection'); 13 | var mgd = MidgardTest.cnc; 14 | var select; 15 | var result; 16 | 17 | var book_qualifier = 'book_q'; 18 | var book_title = 'btitle'; 19 | var book_a_title = 'Book A'; 20 | var book_title_column = 'booktitle'; 21 | var book_store_id = 'book_store_id'; 22 | 23 | var book_store_qualifier = 'bookstore'; 24 | 25 | describe('Midgard.SqlQueryResult - Constraints', function() { 26 | 27 | beforeEach(function(done){ 28 | var tr = new Midgard.Transaction({'connection':mgd}); 29 | tr.begin(); 30 | 31 | var sdirA = Midgard.Object.factory(mgd, 'gir_test_book_store'); 32 | sdirA.name = 'Bookstore'; 33 | var created = sdirA.create(); 34 | created.should.equal(true); 35 | var idA = sdirA.id; 36 | 37 | var sA = Midgard.Object.factory(mgd, 'gir_test_book_crud'); 38 | sA.title = book_a_title; 39 | sA.edition = 1; 40 | sA.store = idA; 41 | created = sA.create(); 42 | created.should.equal(true); 43 | 44 | var sB = Midgard.Object.factory(mgd, 'gir_test_book_crud'); 45 | sB.title = 'Book B'; 46 | sB.edition = 2; 47 | sB.store = idA; 48 | created = sB.create(); 49 | created.should.equal(true); 50 | 51 | var sC = Midgard.Object.factory(mgd, 'gir_test_book_crud'); 52 | sC.title = 'Book C'; 53 | sC.edition = 3; 54 | sC.store = idA; 55 | created = sC.create(); 56 | created.should.equal(true); 57 | 58 | tr.commit(); 59 | done(); 60 | }); 61 | 62 | afterEach(function(done){ 63 | var tr = new Midgard.Transaction({'connection':mgd}); 64 | tr.begin(); 65 | 66 | var st = new Midgard.QueryStorage({'dbclass':'gir_test_book_crud'}); 67 | var qs = new Midgard.QuerySelect({'connection':mgd, 'storage':st}); 68 | qs.execute(); 69 | var objects = qs.list_objects(); 70 | var i = 0; 71 | for (i in objects) { 72 | objects[i].purge(false); 73 | } 74 | 75 | st = new Midgard.QueryStorage({'dbclass':'gir_test_book_store'}); 76 | qs = new Midgard.QuerySelect({'connection':mgd, 'storage':st}); 77 | qs.execute(); 78 | var objects = qs.list_objects(); 79 | var i = 0; 80 | for (i in objects) { 81 | objects[i].purge(false); 82 | } 83 | 84 | tr.commit(); 85 | done(); 86 | }) 87 | 88 | it('execute invalid query', function() { 89 | select = new Midgard.SqlQuerySelectData({'connection':MidgardTest.cnc}); 90 | var storage = new Midgard.QueryStorage({'dbclass':'midgard_person'}); 91 | var column = new Midgard.SqlQueryColumn({ 92 | 'queryproperty': new Midgard.QueryProperty({'property':'firstname', 'storage':storage}), 93 | 'name':'name', 94 | 'qualifier':'p' 95 | }); 96 | select.add_column(column); 97 | select.set_constraint( 98 | new Midgard.SqlQueryConstraint( 99 | {'column': new Midgard.SqlQueryColumn({ 100 | 'queryproperty': new Midgard.QueryProperty({'property':'title'}), 101 | 'qualifier': book_qualifier 102 | }), 103 | 'operator':'=', 104 | 'holder': new Midgard.QueryValue.create_with_value(book_a_title) 105 | }) 106 | ); 107 | try { 108 | select.execute(); 109 | } catch (err) { 110 | err.message.should.not.be.equal(''); 111 | // https://github.com/creationix/node-gir/issues/19 112 | //console.log(err); 113 | //err.should.be.an.instanceof(GObject.Error) 114 | //err.code.should.equal(Midgard.ValidationError.TYPE_INVALID); 115 | } 116 | }); 117 | 118 | it('execute', function() { 119 | select = new Midgard.SqlQuerySelectData({'connection':MidgardTest.cnc}); 120 | var storage = new Midgard.QueryStorage({'dbclass':'midgard_person'}); 121 | var column = new Midgard.SqlQueryColumn({ 122 | 'queryproperty': new Midgard.QueryProperty({'property':'firstname', 'storage':storage}), 123 | 'name':'name', 124 | 'qualifier':'p' 125 | }); 126 | select.add_column(column); 127 | select.execute(); 128 | }); 129 | }); 130 | -------------------------------------------------------------------------------- /tests/test_arguments_directions.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var TestGIR = require('./TestGIR'); 6 | var GdkPixbuf = TestGIR.GdkPixbuf; 7 | var GObject = TestGIR.GObject; 8 | var win = TestGIR.win; 9 | 10 | describe('Arguments direction', function() { 11 | 12 | describe('in', function() { 13 | 14 | it('integer', function() { 15 | win.set_property("default_height", 1); 16 | }); 17 | 18 | it('string', function() { 19 | win.set_property("title", "Lancelot"); 20 | }); 21 | 22 | it('boolean', function() { 23 | win.set_property("modal", true); 24 | }); 25 | 26 | it('double', function() { 27 | win.set_property("opacity", 0.5) 28 | }); 29 | 30 | it('null', function() { 31 | win.set_icon(null); 32 | }); 33 | 34 | it('object', function() { 35 | var pixbuf = new GdkPixbuf.Pixbuf(0, false, 1, 1, 1); 36 | win.set_icon(pixbuf); 37 | }); 38 | }); 39 | 40 | describe('out', function() { 41 | 42 | /* FIXME: Fails with "Error: IN arguments conversion failed" */ 43 | function pendingGetPropertyTest() { 44 | win.set_title("Lancelot"); 45 | var title = null; 46 | win.get_property("title", title); 47 | title.should.equal("Lancelot"); 48 | }; 49 | it('get_property') 50 | 51 | it('integer', function() { 52 | var type = GObject.type_from_name("GtkWindow"); 53 | var children = GObject.type_children(type); // hidden, implicit array length 54 | //children.should.not.equal(null); 55 | children.length.should.not.be.below(1); 56 | children.should.include(GObject.type_from_name('GtkDialog')); 57 | children.should.include(GObject.type_from_name('GtkAssistant')); 58 | children.should.include(GObject.type_from_name('GtkPlug')); 59 | children.should.include(GObject.type_from_name('GtkOffscreenWindow')); 60 | }); 61 | }); 62 | 63 | describe('in out', function() { 64 | 65 | /* FIXME: not implemented yet */ 66 | function pendingInOutIntegerTest () { 67 | var entry = gtk.Entry() 68 | var insert_position = 0 69 | var new_insert_position = entry.insert_text("abc123", -1, insert_position); 70 | new_insert_position.should.be(6) 71 | }; 72 | it('integer') 73 | 74 | }); 75 | 76 | }); 77 | 78 | -------------------------------------------------------------------------------- /tests/test_object_constructor.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var gir = require('../gir'); 6 | gir.init(); 7 | 8 | var GLib = gir.load('GLib'); 9 | var GObject = gir.load('GObject'); 10 | 11 | var Gtk = gir.load('Gtk', '3.0'); 12 | Gtk.init(0); 13 | 14 | describe('Object constructor', function() { 15 | 16 | describe('with "new" operator', function() { 17 | 18 | it('without arguments', function() { 19 | var label = new Gtk.Label(); 20 | label.label.should.equal(""); 21 | }); 22 | 23 | it('with properties', function() { 24 | var label = new Gtk.Label( { label: 'aText99'} ); 25 | label.label.should.equal('aText99'); 26 | }); 27 | }); 28 | 29 | describe('using static functions', function() { 30 | 31 | /* FIXME: pending. Calling .new() ends up calling GObject.Object.new() directly and not 32 | * the .new() of Gtk.Button. Ordering problem in registration of static functions? */ 33 | it('new()', null, function() { 34 | var button = Gtk.Button.new(); 35 | button.label.should.equal(null); 36 | }); 37 | 38 | it('new_with_argument(arg)', function() { 39 | var button = Gtk.Button.new_with_label('myLabel312'); 40 | button.label.should.equal('myLabel312'); 41 | }); 42 | 43 | }); 44 | 45 | describe('with properties', function() { 46 | 47 | it('derived from interface', function() { 48 | var box = new Gtk.Box( { orientation : Gtk.Orientation.vertical } ); 49 | box.orientation.should.equal(Gtk.Orientation.vertical); 50 | }); 51 | }); 52 | 53 | }); 54 | 55 | -------------------------------------------------------------------------------- /tests/test_object_property.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var TestGIR = require('./TestGIR'); 6 | var win = TestGIR.win; 7 | var GdkPixbuf = TestGIR.GdkPixbuf; 8 | 9 | var pixbuf = new GdkPixbuf.Pixbuf(0, false, 1, 1, 1); 10 | 11 | describe('Gtk.Object', function() { 12 | 13 | describe('property', function() { 14 | 15 | describe('boolean', function() { 16 | 17 | it('should be true', function() { 18 | win.modal = true; 19 | win.modal.should.equal(true); 20 | }); 21 | 22 | it('should be false', function() { 23 | win.modal = false; 24 | win.modal.should.equal(false); 25 | }); 26 | }); 27 | 28 | describe('string', function() { 29 | 30 | it('set Lancelot', function() { 31 | win.title = 'Lancelot'; 32 | win.title.should.equal('Lancelot'); 33 | }); 34 | 35 | it('get Lancelot', function() { 36 | win.title.should.equal('Lancelot'); 37 | win.title.should.not.equal(''); 38 | win.title.should.not.equal(' '); 39 | }); 40 | }); 41 | 42 | describe('integer', function() { 43 | 44 | it('set 1', function() { 45 | win['default-height'] = 1; 46 | win['default-height'].should.equal(1); 47 | }); 48 | 49 | it('get 1', function() { 50 | win['default-height'].should.equal(1); 51 | win['default-height'].should.not.equal(0); 52 | win['default-height'].should.not.equal(-1); 53 | }); 54 | }); 55 | 56 | describe('double', function() { 57 | 58 | it('set 0.33', function() { 59 | win.opacity = 0.33; 60 | win.opacity.should.equal(0.33); 61 | }); 62 | 63 | it('get 0.33', function() { 64 | win.opacity.should.equal(0.33); 65 | win.opacity.should.not.equal(0.001); 66 | win.opacity.should.not.equal(2); 67 | win.opacity.should.not.equal(1.23); 68 | }); 69 | }); 70 | 71 | describe('object', function() { 72 | 73 | it('set icon', function() { 74 | win.icon = pixbuf; 75 | }); 76 | 77 | it('get icon', function() { 78 | win.icon.should.be.a('object'); 79 | win.icon.should.not.equal(win); 80 | }); 81 | }); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /tests/test_returned_value.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var TestGIR = require('./TestGIR'); 6 | var win = TestGIR.win; 7 | var GdkPixbuf = TestGIR.GdkPixbuf; 8 | var GObject = TestGIR.GObject; 9 | 10 | var pixbuf = new GdkPixbuf.Pixbuf(0, false, 1, 1, 1); 11 | 12 | describe('Return value', function() { 13 | 14 | it('number', function() { 15 | var int_value = GObject.type_from_name("GtkWindow"); 16 | int_value.should.be.a('number'); 17 | }); 18 | 19 | it('integer', function() { 20 | 21 | }); 22 | 23 | it('char', function() { 24 | win.title = 'Lancelot'; 25 | win.title.should.be.a('string'); 26 | }); 27 | 28 | it('double', function() { 29 | 30 | }); 31 | 32 | it('boolean', function() { 33 | win.get_decorated().should.be.a('boolean'); 34 | }); 35 | 36 | it('GValue', function() { 37 | 38 | }); 39 | 40 | it('null', function() { 41 | win.set_icon(null); 42 | should.strictEqual(null, win.get_icon()); 43 | }); 44 | 45 | it('object', function() { 46 | win.icon = pixbuf; 47 | win.icon.should.be.a('object'); 48 | }); 49 | 50 | it('void', function() { 51 | var void_value = win.resize(10, 10); 52 | should.strictEqual(undefined, void_value); 53 | }); 54 | 55 | describe('array', function() { 56 | it('GType', function() { 57 | var type = GObject.type_from_name("GtkWindow"); 58 | var gtype_array = GObject.type_children(type); 59 | gtype_array.length.should.not.equal(0); 60 | }); 61 | }); 62 | }); 63 | 64 | -------------------------------------------------------------------------------- /tests/test_structure_constructor.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('mocha'), 3 | should = require('should'); 4 | 5 | var gir = require('../gir'); 6 | gir.init(); 7 | 8 | var GLib = gir.load('GLib'); 9 | var GObject = gir.load('GObject'); 10 | 11 | var gtk = gir.load('Gtk', '3.0'); 12 | gtk.init(0); 13 | 14 | describe('Structure constructor', function() { 15 | 16 | describe('with properties', function() { 17 | 18 | it('GLib.MainLoop', function() { 19 | var loop = new GLib.MainLoop(null, false); 20 | loop.should.be.a('object'); 21 | var running = loop.is_running(); 22 | running.should.equal(false); 23 | }); 24 | }); 25 | 26 | describe('without properties', function() { 27 | 28 | it('GLib.MainContext', function() { 29 | var context = new GLib.MainContext(); 30 | context.should.be.a('object'); 31 | }); 32 | }); 33 | }); 34 | 35 | -------------------------------------------------------------------------------- /tests/travis_midgard.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Options 4 | MIDGARD_LIBS_VERSION=10.05.7 5 | MIDGARD_EXT_VERSION=ratatoskr 6 | 7 | # Install Midgard2 library dependencies 8 | sudo apt-get install -y dbus libglib2.0-dev libgda-4.0-4 libgda-4.0-dev libxml2-dev libdbus-1-dev libdbus-glib-1-dev gobject-introspection libgirepository1.0-dev xvfb 9 | 10 | # Build Midgard2 core from recent tarball 11 | wget -q https://github.com/midgardproject/midgard-core/tarball/${MIDGARD_EXT_VERSION} 12 | tar -xzf ratatoskr 13 | sh -c "cd midgardproject-midgard-core-*&&./autogen.sh --prefix=/usr; make; sudo make install" 14 | rm -f ${MIDGARD_EXT_VERSION} 15 | --------------------------------------------------------------------------------