UNPKG

node-gtk

Version:

GNOME Gtk+ bindings for NodeJS

180 lines (139 loc) 4.43 kB
/* * register-class.js */ const snakeCase = require('lodash.snakecase') const internal = require('./native.js') const module_ = require('./module.js') const { GI } = require('./bootstrap.js') const GObject = module_.require('GObject') module.exports = registerClass /** * Create a new GObject type * @param {Class} klass - The class to register * @param {string} [klass.GTypeName] - The name of the GType (klass.name by default) */ function registerClass(klass) { const parent = Object.getPrototypeOf(klass.prototype).constructor if (!(klass.prototype instanceof GObject.Object)) throw new Error(`Invalid base class (${parent.name})`) const name = createGTypeName(klass) const gtype = GObject.typeFromName(name) const parentName = getGTypeName(parent) const parentGtype = GObject.typeFromName(parentName) if (gtype !== GObject.TYPE_INVALID) throw new Error(`GType name already registerd: ${name}`) if (parentGtype === GObject.TYPE_INVALID) throw new Error(`Parent class not registered: ${parent.name}`) // Register the class with the type system const klassGtype = internal.RegisterClass(name, klass, parentName, parent) // Setup our class as the native ones are done klass.prototype.__gtype__ = klassGtype // Setup virtual functions setupVirtualFunctions(klass, klassGtype, parentGtype) } // Helpers function setupVirtualFunctions(klass, klassGtype, parentGtype) { const parentInfo = findInfoByGtype(parentGtype) if (!parentInfo) throw new Error(`Could not find GIR data in inheritance chain`) Object.getOwnPropertyNames(klass.prototype).forEach(key => { if (key === 'constructor') return if (typeof klass.prototype[key] !== 'function') return const nativeName = snakeCase(key) const vfuncInfo = findVFunc(klassGtype, parentInfo, nativeName) if (!vfuncInfo) return internal.RegisterVFunc( vfuncInfo, klassGtype, nativeName, klass.prototype[key] ) }) } function findVFunc(gtype, parentInfo, name) { let vfuncInfo = findVFuncOnParents(parentInfo, name) if (!vfuncInfo) { vfuncInfo = findVFuncOnInterfaces(gtype, name) } return vfuncInfo } function findVFuncOnParents(info, name) { let parent = info /* Since it isn't possible to override a vfunc on * an interface without reimplementing it, we don't need * to search the parent types when looking for a vfunc. */ let [vfunc, _] = GI.object_info_find_vfunc_using_interfaces(parent, name, null) if (vfunc) { return vfunc } while (parent) { vfunc = GI.object_info_find_vfunc(info, name) if (vfunc) { return vfunc } /* HACK: object_info_find_vfunc sometimes fail, so we also search for * the matching entry manually. */ const n = GI.object_info_get_n_vfuncs(parent) for (let i = 0; i < n; i++) { const vfunc = GI.object_info_get_vfunc(parent, i) const currentName = GI.BaseInfo_get_name.call(vfunc) if (currentName === name) { return vfunc } } parent = GI.object_info_get_parent(parent) } return null } function findVFuncOnInterfaces(gtype, name) { const interfaces = GObject.typeInterfaces(gtype); for (i = 0; i < interfaces.length; i++) { const interfaceInfo = findInfoByGtype(interfaces[i]) /* The interface doesn't have to exist, it could be private * or dynamic. */ if (interfaceInfo) { const vfunc = GI.interface_info_find_vfunc(interfaceInfo, name); if (vfunc) return vfunc } } return null } function findInfoByGtype(gtype) { let current = gtype while (current) { const info = GI.Repository_find_by_gtype.call(GI.Repository_get_default(), current) if (info) return info current = GObject.typeParent(current) } return null } function getGTypeName(klass) { const name = klass.hasOwnProperty('GTypeName') ? klass.GTypeName : klass.name if (name) { const sanitized = sanitizeGType(name); if (sanitized !== name) throw new Error(`GTypeName value is invalid: ${name}`) return sanitized } return undefined } let nextId = 1 function createGTypeName(klass) { const name = getGTypeName(klass) if (name) return name const newName = `Anonymous${nextId++}` klass.name = newName return sanitizeGType(newName) } function sanitizeGType(s) { return s.replace(/[^a-z0-9+_-]/gi, '_'); }