diff --git a/src/bindings/girepository.js b/src/bindings/girepository.js index 7833662..a15132c 100644 --- a/src/bindings/girepository.js +++ b/src/bindings/girepository.js @@ -28,6 +28,7 @@ const { g } = openLib(libName("girepository-1.0", 1), { }, base_info: { get_name: $string($pointer), + get_container: $pointer($pointer), is_deprecated: $bool($pointer), get_type: $i32($pointer), ref: $void($pointer), @@ -75,6 +76,7 @@ const { g } = openLib(libName("girepository-1.0", 1), { get_n_fields: $i32($pointer), get_field: $pointer($pointer, $i32), get_size: $i32($pointer), + find_field: $pointer($pointer, $string), }, object_info: { get_n_methods: $i32($pointer), @@ -99,6 +101,7 @@ const { g } = openLib(libName("girepository-1.0", 1), { get_vfunc: $pointer($pointer, $i32), get_n_properties: $i32($pointer), get_property: $pointer($pointer, $i32), + get_iface_struct: $pointer($pointer), }, property_info: { get_flags: $i32($pointer), @@ -110,8 +113,9 @@ const { g } = openLib(libName("girepository-1.0", 1), { field_info: { get_flags: $i32($pointer), get_type: $pointer($pointer), - get_field: $i32($pointer,$pointer, $buffer), + get_field: $bool($pointer,$pointer, $buffer), set_field: $i32($pointer,$pointer, $buffer), + get_offset: $i32($pointer), }, }, }); diff --git a/src/types/callable.js b/src/types/callable.js index 80a26a4..84bb20e 100644 --- a/src/types/callable.js +++ b/src/types/callable.js @@ -1,3 +1,4 @@ +import { cast_ptr_u64, deref_buf } from "../base_utils/convert.ts"; import { GIDirection, GIFunctionInfoFlags, @@ -11,6 +12,7 @@ import { createConstructor } from "./callable/constructor.js"; import { createFunction } from "./callable/function.js"; import { createMethod } from "./callable/method.js"; import { createVFunc } from "./callable/vfunc.js"; +import { createCallback } from "./callback.js"; export function createArg(info) { const type = g.arg_info.get_type(info); @@ -120,19 +122,63 @@ export function handleCallable(target, info) { } case GIInfoType.VFUNC: { - if (Object.hasOwn(target.prototype, name)) { - return; - } - const value = createVFunc(info); - Object.defineProperty(target.prototype, name, { + Object.defineProperty(target.prototype, `vfunc_` + name, { enumerable: true, - value(...args) { - return value( - Reflect.getOwnMetadata("gi:ref", this), - Reflect.getOwnMetadata("gi:gtype", this.constructor), - ...args, + get() { + return (...args) => { + return value( + Reflect.getOwnMetadata("gi:ref", this), + Reflect.getOwnMetadata("gi:gtype", this.constructor), + ...args, + ); + }; + }, + set(value) { + const cName = g.base_info.get_name(info); + + const containerInfo = g.base_info.get_container(info); + const containerType = g.base_info.get_type(containerInfo); + + let containerStruct, pointer; + + if (containerType === GIInfoType.INTERFACE) { + // we are setting a vfunc provided by an interface + containerStruct = g.interface_info.get_iface_struct(containerInfo); + const klass = g.type_class.ref( + Reflect.getOwnMetadata("gi:gtype", this.constructor), + ); + // get the pointer to the interface struct of this class + pointer = g.type_interface.peek( + klass, + g.registered_type_info.get_g_type(containerInfo), + ); + } else { + // we are directly setting a vfunc provided by a class + containerStruct = g.object_info.get_class_struct(containerInfo); + pointer = g.type_class.ref( + Reflect.getOwnMetadata("gi:gtype", this.constructor), + ); + } + + const fieldInfo = g.struct_info.find_field(containerStruct, cName); + + if (!fieldInfo) { + // This vfunc doesn't have a corresponding field in the class or + // interface struct + return; + } + + const cb = createCallback(info, value, this); + const offset = g.field_info.get_offset(fieldInfo); + const dataView = new ExtendedDataView( + deref_buf( + pointer, + offset + 8, + offset, + ), ); + dataView.setBigUint64(cast_ptr_u64(cb.pointer)); }, }); return; diff --git a/src/types/field.js b/src/types/field.js index 3371223..db9ef45 100644 --- a/src/types/field.js +++ b/src/types/field.js @@ -1,7 +1,10 @@ -import { GFieldInfoFlags } from "../bindings/enums.js"; +import { cast_ptr_u64, deref_buf } from "../base_utils/convert.ts"; +import { GFieldInfoFlags, GIInfoType, GITypeTag } from "../bindings/enums.js"; import g from "../bindings/mod.js"; +import { ExtendedDataView } from "../utils/dataview.js"; import { getName } from "../utils/string.ts"; import { boxArgument, unboxArgument } from "./argument.js"; +import { createCallback } from "./callback.js"; export function handleField( target, @@ -18,6 +21,7 @@ export function handleField( Object.defineProperty(target.prototype, name, { enumerable: true, + configurable: true, get() { if (!(flags & GFieldInfoFlags.READABLE)) { throw new Error(`Field ${name} is not readable`); @@ -44,11 +48,50 @@ export function handleField( throw new Error(`Field ${name} is not writable`); } - g.field_info.set_field( - fieldInfo, - Reflect.getOwnMetadata("gi:ref", this), - boxArgument(type, value), - ); + const tag = g.type_info.get_tag(type); + + switch (tag) { + case GITypeTag.OBJECT: + case GITypeTag.STRUCT: { + console.warn(`cannot set complex field: ${name}`); + break; + } + case GITypeTag.INTERFACE: { + const info = g.type_info.get_interface(type); + switch (g.base_info.get_type(info)) { + case GIInfoType.CALLBACK: { + // create a callback and set it to the field's pointer + const cb = createCallback(info, value); + const offset = g.field_info.get_offset(fieldInfo); + const dataView = new ExtendedDataView( + deref_buf( + Reflect.getOwnMetadata("gi:ref", this), + offset + 8, + offset, + ), + ); + dataView.setBigUint64(cast_ptr_u64(cb.pointer)); + break; + } + default: { + console.warn(`cannot set complex field: ${name}`); + break; + } + } + g.base_info.unref(info); + break; + } + default: { + const boxed = boxArgument(type, value); + + g.field_info.set_field( + fieldInfo, + Reflect.getOwnMetadata("gi:ref", this), + boxed, + ); + break; + } + } }, }); }