@gtkx/codegen 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +373 -0
- package/dist/dsl/context.d.ts +104 -0
- package/dist/dsl/context.d.ts.map +1 -0
- package/dist/dsl/context.js +140 -0
- package/dist/dsl/context.js.map +1 -0
- package/dist/dsl/emit.d.ts +28 -0
- package/dist/dsl/emit.d.ts.map +1 -0
- package/dist/dsl/emit.js +41 -0
- package/dist/dsl/emit.js.map +1 -0
- package/dist/dsl/identifier.d.ts +28 -0
- package/dist/dsl/identifier.d.ts.map +1 -0
- package/dist/dsl/identifier.js +45 -0
- package/dist/dsl/identifier.js.map +1 -0
- package/dist/dsl/imports.d.ts +58 -0
- package/dist/dsl/imports.d.ts.map +1 -0
- package/dist/dsl/imports.js +107 -0
- package/dist/dsl/imports.js.map +1 -0
- package/dist/dsl/module.d.ts +53 -0
- package/dist/dsl/module.d.ts.map +1 -0
- package/dist/dsl/module.js +76 -0
- package/dist/dsl/module.js.map +1 -0
- package/dist/ffi/pipeline.d.ts +19 -0
- package/dist/ffi/pipeline.d.ts.map +1 -0
- package/dist/ffi/pipeline.js +107 -0
- package/dist/ffi/pipeline.js.map +1 -0
- package/dist/fingerprint.d.ts +30 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +33 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/gi-store.d.ts +34 -0
- package/dist/gi-store.d.ts.map +1 -0
- package/dist/gi-store.js +154 -0
- package/dist/gi-store.js.map +1 -0
- package/dist/gir/boxed.d.ts +66 -0
- package/dist/gir/boxed.d.ts.map +1 -0
- package/dist/gir/boxed.js +49 -0
- package/dist/gir/boxed.js.map +1 -0
- package/dist/gir/callback.d.ts +15 -0
- package/dist/gir/callback.d.ts.map +1 -0
- package/dist/gir/callback.js +17 -0
- package/dist/gir/callback.js.map +1 -0
- package/dist/gir/class.d.ts +49 -0
- package/dist/gir/class.d.ts.map +1 -0
- package/dist/gir/class.js +42 -0
- package/dist/gir/class.js.map +1 -0
- package/dist/gir/enum.d.ts +39 -0
- package/dist/gir/enum.d.ts.map +1 -0
- package/dist/gir/enum.js +24 -0
- package/dist/gir/enum.js.map +1 -0
- package/dist/gir/field.d.ts +27 -0
- package/dist/gir/field.d.ts.map +1 -0
- package/dist/gir/field.js +19 -0
- package/dist/gir/field.js.map +1 -0
- package/dist/gir/function.d.ts +47 -0
- package/dist/gir/function.d.ts.map +1 -0
- package/dist/gir/function.js +28 -0
- package/dist/gir/function.js.map +1 -0
- package/dist/gir/namespace.d.ts +65 -0
- package/dist/gir/namespace.d.ts.map +1 -0
- package/dist/gir/namespace.js +60 -0
- package/dist/gir/namespace.js.map +1 -0
- package/dist/gir/parameter.d.ts +82 -0
- package/dist/gir/parameter.d.ts.map +1 -0
- package/dist/gir/parameter.js +66 -0
- package/dist/gir/parameter.js.map +1 -0
- package/dist/gir/parse.d.ts +78 -0
- package/dist/gir/parse.d.ts.map +1 -0
- package/dist/gir/parse.js +155 -0
- package/dist/gir/parse.js.map +1 -0
- package/dist/gir/primitives.d.ts +36 -0
- package/dist/gir/primitives.d.ts.map +1 -0
- package/dist/gir/primitives.js +109 -0
- package/dist/gir/primitives.js.map +1 -0
- package/dist/gir/property.d.ts +29 -0
- package/dist/gir/property.d.ts.map +1 -0
- package/dist/gir/property.js +16 -0
- package/dist/gir/property.js.map +1 -0
- package/dist/gir/qualified-name.d.ts +37 -0
- package/dist/gir/qualified-name.d.ts.map +1 -0
- package/dist/gir/qualified-name.js +39 -0
- package/dist/gir/qualified-name.js.map +1 -0
- package/dist/gir/qualify.d.ts +32 -0
- package/dist/gir/qualify.d.ts.map +1 -0
- package/dist/gir/qualify.js +60 -0
- package/dist/gir/qualify.js.map +1 -0
- package/dist/gir/repository.d.ts +82 -0
- package/dist/gir/repository.d.ts.map +1 -0
- package/dist/gir/repository.js +122 -0
- package/dist/gir/repository.js.map +1 -0
- package/dist/gir/signal.d.ts +22 -0
- package/dist/gir/signal.d.ts.map +1 -0
- package/dist/gir/signal.js +20 -0
- package/dist/gir/signal.js.map +1 -0
- package/dist/gir/size.d.ts +67 -0
- package/dist/gir/size.d.ts.map +1 -0
- package/dist/gir/size.js +87 -0
- package/dist/gir/size.js.map +1 -0
- package/dist/gir/type-ref.d.ts +79 -0
- package/dist/gir/type-ref.d.ts.map +1 -0
- package/dist/gir/type-ref.js +101 -0
- package/dist/gir/type-ref.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/jsx-store.d.ts +22 -0
- package/dist/jsx-store.d.ts.map +1 -0
- package/dist/jsx-store.js +40 -0
- package/dist/jsx-store.js.map +1 -0
- package/dist/react/compounds-meta.d.ts +18 -0
- package/dist/react/compounds-meta.d.ts.map +1 -0
- package/dist/react/compounds-meta.js +93 -0
- package/dist/react/compounds-meta.js.map +1 -0
- package/dist/react/compounds.d.ts +27 -0
- package/dist/react/compounds.d.ts.map +1 -0
- package/dist/react/compounds.js +164 -0
- package/dist/react/compounds.js.map +1 -0
- package/dist/react/internal.d.ts +14 -0
- package/dist/react/internal.d.ts.map +1 -0
- package/dist/react/internal.js +79 -0
- package/dist/react/internal.js.map +1 -0
- package/dist/react/jsx.d.ts +22 -0
- package/dist/react/jsx.d.ts.map +1 -0
- package/dist/react/jsx.js +134 -0
- package/dist/react/jsx.js.map +1 -0
- package/dist/react/pipeline.d.ts +13 -0
- package/dist/react/pipeline.d.ts.map +1 -0
- package/dist/react/pipeline.js +25 -0
- package/dist/react/pipeline.js.map +1 -0
- package/dist/react/props.d.ts +38 -0
- package/dist/react/props.d.ts.map +1 -0
- package/dist/react/props.js +231 -0
- package/dist/react/props.js.map +1 -0
- package/dist/react/slot-props.d.ts +10 -0
- package/dist/react/slot-props.d.ts.map +1 -0
- package/dist/react/slot-props.js +56 -0
- package/dist/react/slot-props.js.map +1 -0
- package/dist/react/widgets.d.ts +83 -0
- package/dist/react/widgets.d.ts.map +1 -0
- package/dist/react/widgets.js +152 -0
- package/dist/react/widgets.js.map +1 -0
- package/dist/runner.d.ts +55 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +60 -0
- package/dist/runner.js.map +1 -0
- package/dist/store-fs.d.ts +109 -0
- package/dist/store-fs.d.ts.map +1 -0
- package/dist/store-fs.js +98 -0
- package/dist/store-fs.js.map +1 -0
- package/dist/transpile.d.ts +32 -0
- package/dist/transpile.d.ts.map +1 -0
- package/dist/transpile.js +63 -0
- package/dist/transpile.js.map +1 -0
- package/dist/typecheck-store.d.ts +41 -0
- package/dist/typecheck-store.d.ts.map +1 -0
- package/dist/typecheck-store.js +102 -0
- package/dist/typecheck-store.js.map +1 -0
- package/dist/writers/alias.d.ts +13 -0
- package/dist/writers/alias.d.ts.map +1 -0
- package/dist/writers/alias.js +17 -0
- package/dist/writers/alias.js.map +1 -0
- package/dist/writers/async.d.ts +22 -0
- package/dist/writers/async.d.ts.map +1 -0
- package/dist/writers/async.js +48 -0
- package/dist/writers/async.js.map +1 -0
- package/dist/writers/boxed-constructor.d.ts +26 -0
- package/dist/writers/boxed-constructor.d.ts.map +1 -0
- package/dist/writers/boxed-constructor.js +85 -0
- package/dist/writers/boxed-constructor.js.map +1 -0
- package/dist/writers/boxed-field-accessor.d.ts +18 -0
- package/dist/writers/boxed-field-accessor.d.ts.map +1 -0
- package/dist/writers/boxed-field-accessor.js +260 -0
- package/dist/writers/boxed-field-accessor.js.map +1 -0
- package/dist/writers/boxed-layout.d.ts +27 -0
- package/dist/writers/boxed-layout.d.ts.map +1 -0
- package/dist/writers/boxed-layout.js +128 -0
- package/dist/writers/boxed-layout.js.map +1 -0
- package/dist/writers/boxed.d.ts +20 -0
- package/dist/writers/boxed.d.ts.map +1 -0
- package/dist/writers/boxed.js +71 -0
- package/dist/writers/boxed.js.map +1 -0
- package/dist/writers/callables.d.ts +124 -0
- package/dist/writers/callables.d.ts.map +1 -0
- package/dist/writers/callables.js +283 -0
- package/dist/writers/callables.js.map +1 -0
- package/dist/writers/callback.d.ts +16 -0
- package/dist/writers/callback.d.ts.map +1 -0
- package/dist/writers/callback.js +38 -0
- package/dist/writers/callback.js.map +1 -0
- package/dist/writers/class-struct-record.d.ts +31 -0
- package/dist/writers/class-struct-record.d.ts.map +1 -0
- package/dist/writers/class-struct-record.js +67 -0
- package/dist/writers/class-struct-record.js.map +1 -0
- package/dist/writers/class-struct.d.ts +22 -0
- package/dist/writers/class-struct.d.ts.map +1 -0
- package/dist/writers/class-struct.js +85 -0
- package/dist/writers/class-struct.js.map +1 -0
- package/dist/writers/class.d.ts +16 -0
- package/dist/writers/class.d.ts.map +1 -0
- package/dist/writers/class.js +307 -0
- package/dist/writers/class.js.map +1 -0
- package/dist/writers/constant.d.ts +15 -0
- package/dist/writers/constant.d.ts.map +1 -0
- package/dist/writers/constant.js +23 -0
- package/dist/writers/constant.js.map +1 -0
- package/dist/writers/constants.d.ts +15 -0
- package/dist/writers/constants.d.ts.map +1 -0
- package/dist/writers/constants.js +23 -0
- package/dist/writers/constants.js.map +1 -0
- package/dist/writers/constructor-props.d.ts +32 -0
- package/dist/writers/constructor-props.d.ts.map +1 -0
- package/dist/writers/constructor-props.js +110 -0
- package/dist/writers/constructor-props.js.map +1 -0
- package/dist/writers/enum.d.ts +21 -0
- package/dist/writers/enum.d.ts.map +1 -0
- package/dist/writers/enum.js +48 -0
- package/dist/writers/enum.js.map +1 -0
- package/dist/writers/function.d.ts +40 -0
- package/dist/writers/function.d.ts.map +1 -0
- package/dist/writers/function.js +127 -0
- package/dist/writers/function.js.map +1 -0
- package/dist/writers/gtype-binding.d.ts +19 -0
- package/dist/writers/gtype-binding.d.ts.map +1 -0
- package/dist/writers/gtype-binding.js +40 -0
- package/dist/writers/gtype-binding.js.map +1 -0
- package/dist/writers/inheritance.d.ts +48 -0
- package/dist/writers/inheritance.d.ts.map +1 -0
- package/dist/writers/inheritance.js +90 -0
- package/dist/writers/inheritance.js.map +1 -0
- package/dist/writers/interface.d.ts +16 -0
- package/dist/writers/interface.d.ts.map +1 -0
- package/dist/writers/interface.js +155 -0
- package/dist/writers/interface.js.map +1 -0
- package/dist/writers/method.d.ts +150 -0
- package/dist/writers/method.d.ts.map +1 -0
- package/dist/writers/method.js +510 -0
- package/dist/writers/method.js.map +1 -0
- package/dist/writers/param-classify.d.ts +93 -0
- package/dist/writers/param-classify.d.ts.map +1 -0
- package/dist/writers/param-classify.js +163 -0
- package/dist/writers/param-classify.js.map +1 -0
- package/dist/writers/property-accessor.d.ts +19 -0
- package/dist/writers/property-accessor.d.ts.map +1 -0
- package/dist/writers/property-accessor.js +117 -0
- package/dist/writers/property-accessor.js.map +1 -0
- package/dist/writers/registration.d.ts +27 -0
- package/dist/writers/registration.d.ts.map +1 -0
- package/dist/writers/registration.js +30 -0
- package/dist/writers/registration.js.map +1 -0
- package/dist/writers/return-wrap.d.ts +39 -0
- package/dist/writers/return-wrap.d.ts.map +1 -0
- package/dist/writers/return-wrap.js +152 -0
- package/dist/writers/return-wrap.js.map +1 -0
- package/dist/writers/runtime-override.d.ts +18 -0
- package/dist/writers/runtime-override.d.ts.map +1 -0
- package/dist/writers/runtime-override.js +44 -0
- package/dist/writers/runtime-override.js.map +1 -0
- package/dist/writers/signal.d.ts +21 -0
- package/dist/writers/signal.d.ts.map +1 -0
- package/dist/writers/signal.js +260 -0
- package/dist/writers/signal.js.map +1 -0
- package/dist/writers/ts-type.d.ts +50 -0
- package/dist/writers/ts-type.d.ts.map +1 -0
- package/dist/writers/ts-type.js +75 -0
- package/dist/writers/ts-type.js.map +1 -0
- package/dist/writers/types-ts.d.ts +27 -0
- package/dist/writers/types-ts.d.ts.map +1 -0
- package/dist/writers/types-ts.js +97 -0
- package/dist/writers/types-ts.js.map +1 -0
- package/dist/writers/value.d.ts +89 -0
- package/dist/writers/value.d.ts.map +1 -0
- package/dist/writers/value.js +402 -0
- package/dist/writers/value.js.map +1 -0
- package/package.json +55 -0
- package/src/dsl/context.ts +146 -0
- package/src/dsl/emit.ts +42 -0
- package/src/dsl/identifier.ts +52 -0
- package/src/dsl/imports.ts +109 -0
- package/src/dsl/module.ts +75 -0
- package/src/ffi/pipeline.ts +113 -0
- package/src/fingerprint.ts +50 -0
- package/src/gi-store.ts +199 -0
- package/src/gir/boxed.ts +94 -0
- package/src/gir/callback.ts +26 -0
- package/src/gir/class.ts +79 -0
- package/src/gir/enum.ts +55 -0
- package/src/gir/field.ts +40 -0
- package/src/gir/function.ts +67 -0
- package/src/gir/namespace.ts +118 -0
- package/src/gir/parameter.ts +120 -0
- package/src/gir/parse.ts +171 -0
- package/src/gir/primitives.ts +137 -0
- package/src/gir/property.ts +39 -0
- package/src/gir/qualified-name.ts +55 -0
- package/src/gir/qualify.ts +64 -0
- package/src/gir/repository.ts +149 -0
- package/src/gir/signal.ts +36 -0
- package/src/gir/size.ts +129 -0
- package/src/gir/type-ref.ts +180 -0
- package/src/index.ts +16 -0
- package/src/jsx-store.ts +53 -0
- package/src/react/compounds-meta.ts +99 -0
- package/src/react/compounds.ts +204 -0
- package/src/react/internal.ts +91 -0
- package/src/react/jsx.ts +155 -0
- package/src/react/pipeline.ts +29 -0
- package/src/react/props.ts +307 -0
- package/src/react/slot-props.ts +57 -0
- package/src/react/widgets.ts +185 -0
- package/src/runner.ts +89 -0
- package/src/store-fs.ts +157 -0
- package/src/transpile.ts +80 -0
- package/src/typecheck-store.ts +136 -0
- package/src/writers/alias.ts +19 -0
- package/src/writers/async.ts +44 -0
- package/src/writers/boxed-constructor.ts +105 -0
- package/src/writers/boxed-field-accessor.ts +306 -0
- package/src/writers/boxed-layout.ts +166 -0
- package/src/writers/boxed.ts +77 -0
- package/src/writers/callables.ts +339 -0
- package/src/writers/callback.ts +40 -0
- package/src/writers/class-struct-record.ts +75 -0
- package/src/writers/class-struct.ts +96 -0
- package/src/writers/class.ts +384 -0
- package/src/writers/constant.ts +29 -0
- package/src/writers/constructor-props.ts +130 -0
- package/src/writers/enum.ts +53 -0
- package/src/writers/function.ts +136 -0
- package/src/writers/gtype-binding.ts +48 -0
- package/src/writers/inheritance.ts +104 -0
- package/src/writers/interface.ts +168 -0
- package/src/writers/method.ts +678 -0
- package/src/writers/param-classify.ts +178 -0
- package/src/writers/property-accessor.ts +139 -0
- package/src/writers/registration.ts +42 -0
- package/src/writers/return-wrap.ts +180 -0
- package/src/writers/runtime-override.ts +59 -0
- package/src/writers/signal.ts +318 -0
- package/src/writers/ts-type.ts +115 -0
- package/src/writers/value.ts +485 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { quote } from "@gtkx/utils";
|
|
2
|
+
import type { ModuleContext } from "../dsl/context.js";
|
|
3
|
+
import { arrayLiteral, indent, joinArgs } from "../dsl/emit.js";
|
|
4
|
+
import { namespaceFunctionExportName } from "../dsl/identifier.js";
|
|
5
|
+
import type { GirFunction } from "../gir/function.js";
|
|
6
|
+
import type { GirNamespace } from "../gir/namespace.js";
|
|
7
|
+
import type { GirParameter } from "../gir/parameter.js";
|
|
8
|
+
import { callableReferencesClassStruct } from "./class-struct-record.js";
|
|
9
|
+
import { renderMethodBody, renderMethodReturnType, renderMethodSignature } from "./method.js";
|
|
10
|
+
import { closureAndDestroyIndices, needsRefArg } from "./param-classify.js";
|
|
11
|
+
import { renderFfiType, renderSelfFfiType, renderTrampolineType } from "./value.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Renders the FFI argument list for a callable, as a TypeScript array
|
|
15
|
+
* literal suitable as the third argument to `t.fn(...)`.
|
|
16
|
+
*
|
|
17
|
+
* Includes the instance parameter (if any), every regular parameter, and
|
|
18
|
+
* the implicit `GError**` ref when `throws=1`.
|
|
19
|
+
*
|
|
20
|
+
* @param context - The module context
|
|
21
|
+
* @param fn - The callable
|
|
22
|
+
*/
|
|
23
|
+
const renderArgsLiteral = (context: ModuleContext, fn: GirFunction): string => {
|
|
24
|
+
const args: string[] = [];
|
|
25
|
+
const instanceOffset = fn.instance === undefined ? 0 : 1;
|
|
26
|
+
if (fn.instance !== undefined) {
|
|
27
|
+
context.addRuntimeImport("t");
|
|
28
|
+
args.push(`{ type: ${renderSelfFfiType(context, fn.instance)} }`);
|
|
29
|
+
}
|
|
30
|
+
const closureIndices = closureAndDestroyIndices(fn);
|
|
31
|
+
fn.parameters.forEach((parameter, index) => {
|
|
32
|
+
if (parameter.isVarargs) return;
|
|
33
|
+
if (closureIndices.has(index)) return;
|
|
34
|
+
const optional = parameter.direction === "in" && (parameter.nullable || parameter.optional);
|
|
35
|
+
args.push(argLiteral(context, parameter, optional, instanceOffset));
|
|
36
|
+
});
|
|
37
|
+
if (fn.throws) {
|
|
38
|
+
context.addRuntimeImport("t");
|
|
39
|
+
args.push(`{ type: t.ref(t.boxed("GError", "full", "libgobject-2.0.so.0", "g_error_get_type")) }`);
|
|
40
|
+
}
|
|
41
|
+
return arrayLiteral(args);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const argLiteral = (
|
|
45
|
+
context: ModuleContext,
|
|
46
|
+
parameter: GirParameter,
|
|
47
|
+
optional: boolean,
|
|
48
|
+
instanceOffset: number,
|
|
49
|
+
): string => {
|
|
50
|
+
const trampoline = renderTrampolineType(context, parameter.type, parameter);
|
|
51
|
+
const inner = trampoline ?? renderFfiType(context, parameter.type, parameter.transferOwnership, instanceOffset);
|
|
52
|
+
const refWrapped = needsRefArg(context, parameter);
|
|
53
|
+
const expression = refWrapped ? `t.ref(${inner})` : inner;
|
|
54
|
+
return optional && !refWrapped ? `{ type: ${expression}, optional: true }` : `{ type: ${expression} }`;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Renders the `t.fn(library, symbol, args, returnType)` expression for a
|
|
59
|
+
* callable. Returns `undefined` when the callable cannot be bound (no C
|
|
60
|
+
* identifier or missing namespace shared-library).
|
|
61
|
+
*
|
|
62
|
+
* @param context - The module context
|
|
63
|
+
* @param fn - The callable
|
|
64
|
+
*/
|
|
65
|
+
export const renderFnExpression = (context: ModuleContext, fn: GirFunction): string | undefined => {
|
|
66
|
+
if (fn.cIdentifier === undefined) return undefined;
|
|
67
|
+
const library = context.namespace.sharedLibrary;
|
|
68
|
+
if (library === undefined) return undefined;
|
|
69
|
+
context.addRuntimeImport("t");
|
|
70
|
+
const args = renderArgsLiteral(context, fn);
|
|
71
|
+
const returnType = renderFfiType(
|
|
72
|
+
context,
|
|
73
|
+
fn.returnValue.type,
|
|
74
|
+
fn.returnValue.transferOwnership,
|
|
75
|
+
fn.instance === undefined ? 0 : 1,
|
|
76
|
+
);
|
|
77
|
+
return `t.fn(${joinArgs([quote(library), quote(fn.cIdentifier), args, returnType])})`;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Emits both the bound `const fooBinding = t.fn(...)` and the
|
|
82
|
+
* `export function camelName(...)` wrapper for a namespace-level callable.
|
|
83
|
+
*
|
|
84
|
+
* Silently skipped when {@link renderFnExpression} cannot bind the callable
|
|
85
|
+
* (e.g. macros, unintrospectable signatures) or when the GIR marks the
|
|
86
|
+
* callable as `introspectable="0"`.
|
|
87
|
+
*
|
|
88
|
+
* @param context - The module context
|
|
89
|
+
* @param fn - The callable
|
|
90
|
+
*/
|
|
91
|
+
export const emitNamespaceFunction = (context: ModuleContext, fn: GirFunction): void => {
|
|
92
|
+
if (!fn.introspectable) return;
|
|
93
|
+
if (fn.shadowedBy !== undefined) return;
|
|
94
|
+
if (callableReferencesClassStruct(context, fn)) return;
|
|
95
|
+
const expression = renderFnExpression(context, fn);
|
|
96
|
+
if (expression === undefined) return;
|
|
97
|
+
const bindingName = fn.cIdentifier;
|
|
98
|
+
if (bindingName === undefined) return;
|
|
99
|
+
context.module.appendBinding(`const ${bindingName} = ${expression};`, bindingName);
|
|
100
|
+
|
|
101
|
+
const exportName = namespaceFunctionExportName(bindingName, fn.name, context.namespace.cSymbolPrefixes);
|
|
102
|
+
const signature = renderMethodSignature(context, fn);
|
|
103
|
+
const returnType = renderMethodReturnType(context, fn);
|
|
104
|
+
const body = renderMethodBody(context, fn, { bindingExpression: bindingName, isStatic: true });
|
|
105
|
+
context.module.appendDeclaration(
|
|
106
|
+
`export function ${exportName}(${signature}): ${returnType} {\n${indent(body, 1)}\n}`,
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Emits a namespace's self-bootstrap statements as module-load side effects:
|
|
112
|
+
* a call to its zero-argument `init` entry point and a `whenStopped`
|
|
113
|
+
* registration for its zero-argument `finalize` entry point.
|
|
114
|
+
*
|
|
115
|
+
* GTK-style libraries expose top-level `init`/`finalize` functions (`gtk_init`,
|
|
116
|
+
* `adw_init`, `gtk_source_finalize`, …). Calling `init()` as the module is
|
|
117
|
+
* imported initializes the library's runtime before any of its types are
|
|
118
|
+
* touched — without it a `Gdk` display lookup can run before `gtk_init`.
|
|
119
|
+
* Deferring `finalize` to `whenStopped` runs it during shutdown.
|
|
120
|
+
*
|
|
121
|
+
* @param context - The module context
|
|
122
|
+
* @param namespace - The namespace being generated
|
|
123
|
+
*/
|
|
124
|
+
export const emitNamespaceBootstrap = (context: ModuleContext, namespace: GirNamespace): void => {
|
|
125
|
+
for (const fn of namespace.functions) {
|
|
126
|
+
if (fn.parameters.length > 0) continue;
|
|
127
|
+
if (!fn.introspectable || fn.shadowedBy !== undefined || fn.cIdentifier === undefined) continue;
|
|
128
|
+
const exportName = namespaceFunctionExportName(fn.cIdentifier, fn.name, context.namespace.cSymbolPrefixes);
|
|
129
|
+
if (fn.name === "init") {
|
|
130
|
+
context.module.appendRegistration(`${exportName}();`);
|
|
131
|
+
} else if (fn.name === "finalize") {
|
|
132
|
+
context.module.imports.addNamed("@gtkx/ffi", "whenStopped");
|
|
133
|
+
context.module.appendRegistration(`whenStopped().then(${exportName});`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { quote } from "@gtkx/utils";
|
|
2
|
+
import type { ModuleContext } from "../dsl/context.js";
|
|
3
|
+
|
|
4
|
+
const TYPE_FROM_NAME_BINDING = "g_type_from_name";
|
|
5
|
+
const TYPE_FROM_NAME_LIB = "libgobject-2.0.so.0";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Renders the JS expression that resolves a class's `GType`, appending any
|
|
9
|
+
* required `t.fn` bindings to the module.
|
|
10
|
+
*
|
|
11
|
+
* For regular get-type symbols (`g_object_get_type`, `g_array_get_type`, …)
|
|
12
|
+
* the helper appends a `t.fn(...)` binding for the symbol and returns the bare
|
|
13
|
+
* function reference (`g_object_get_type`), which the runtime invokes once. For
|
|
14
|
+
* the GIR sentinel `"intern"` — used on `GVariant` and a few other types whose
|
|
15
|
+
* `GType` is registered intrinsically — it appends a binding for
|
|
16
|
+
* `g_type_from_name` and returns a `() => g_type_from_name("…")` thunk. Returns
|
|
17
|
+
* `undefined` when neither path is available.
|
|
18
|
+
*
|
|
19
|
+
* @param context - The module context
|
|
20
|
+
* @param getType - The C symbol name or the GIR sentinel `"intern"`
|
|
21
|
+
* @param glibTypeName - The GLib type name (for `"intern"` get-types)
|
|
22
|
+
*/
|
|
23
|
+
export const renderGetTypeReference = (
|
|
24
|
+
context: ModuleContext,
|
|
25
|
+
getType: string,
|
|
26
|
+
glibTypeName: string | undefined,
|
|
27
|
+
): string | undefined => {
|
|
28
|
+
if (getType === "intern" || getType === "") {
|
|
29
|
+
if (glibTypeName === undefined) return undefined;
|
|
30
|
+
appendGTypeFromNameBinding(context);
|
|
31
|
+
return `() => ${TYPE_FROM_NAME_BINDING}(${quote(glibTypeName)})`;
|
|
32
|
+
}
|
|
33
|
+
appendGetTypeBinding(context, getType);
|
|
34
|
+
return getType;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const appendGetTypeBinding = (context: ModuleContext, getType: string): void => {
|
|
38
|
+
const lib = context.namespace.sharedLibrary ?? "";
|
|
39
|
+
const expression = `t.fn(${quote(lib)}, ${quote(getType)}, [], t.uint64)`;
|
|
40
|
+
context.module.appendBinding(`const ${getType} = ${expression};`, getType);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const appendGTypeFromNameBinding = (context: ModuleContext): void => {
|
|
44
|
+
const expression =
|
|
45
|
+
`t.fn(${quote(TYPE_FROM_NAME_LIB)}, ${quote(TYPE_FROM_NAME_BINDING)}, ` +
|
|
46
|
+
`[{ type: t.string("borrowed") }], t.uint64)`;
|
|
47
|
+
context.module.appendBinding(`const ${TYPE_FROM_NAME_BINDING} = ${expression};`, TYPE_FROM_NAME_BINDING);
|
|
48
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { toCamelCase, toIdentifier } from "@gtkx/utils";
|
|
2
|
+
import type { ModuleContext } from "../dsl/context.js";
|
|
3
|
+
import type { GirClass } from "../gir/class.js";
|
|
4
|
+
import type { GirProperty } from "../gir/property.js";
|
|
5
|
+
import { type ResolvedQualifiedClass, resolveQualifiedClass, splitQualifiedName } from "../gir/qualified-name.js";
|
|
6
|
+
import { qualifyTypeRef } from "../gir/qualify.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A directly-implemented interface resolved to its declaration and the
|
|
10
|
+
* namespace that declares it.
|
|
11
|
+
*/
|
|
12
|
+
export type ResolvedInterface = {
|
|
13
|
+
readonly klass: GirClass;
|
|
14
|
+
readonly namespaceName: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Resolves an `<implements>` entry to its `<interface>` declaration.
|
|
19
|
+
*
|
|
20
|
+
* Returns `undefined` when the name resolves to a non-interface entity or
|
|
21
|
+
* cannot be resolved at all.
|
|
22
|
+
*
|
|
23
|
+
* @param context - The module context
|
|
24
|
+
* @param name - The (possibly cross-namespace) interface name
|
|
25
|
+
*/
|
|
26
|
+
export const resolveImplementedInterface = (
|
|
27
|
+
context: ModuleContext,
|
|
28
|
+
name: string,
|
|
29
|
+
defaultNamespace: string = context.namespace.name,
|
|
30
|
+
): ResolvedInterface | undefined => {
|
|
31
|
+
const { namespaceName, typeName } = splitQualifiedName(name, defaultNamespace);
|
|
32
|
+
const resolved = context.repository.resolveNamed(namespaceName, typeName);
|
|
33
|
+
if (resolved === undefined || resolved.kind !== "interface") return undefined;
|
|
34
|
+
return { klass: resolved.value, namespaceName: resolved.namespace.name };
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Invokes `visit` for each ancestor class of `klass`, nearest first.
|
|
39
|
+
*
|
|
40
|
+
* Walks the same-namespace and cross-namespace parent chain, stopping at the
|
|
41
|
+
* first unresolved parent or a cycle. Each ancestor is reported together with
|
|
42
|
+
* the namespace it was resolved through so callers can qualify its references.
|
|
43
|
+
*
|
|
44
|
+
* @param context - The module context
|
|
45
|
+
* @param klass - The class whose ancestors to visit
|
|
46
|
+
* @param visit - Callback invoked once per resolved ancestor
|
|
47
|
+
*/
|
|
48
|
+
export const forEachAncestor = (
|
|
49
|
+
context: ModuleContext,
|
|
50
|
+
klass: GirClass,
|
|
51
|
+
visit: (ancestor: ResolvedQualifiedClass) => void,
|
|
52
|
+
): void => {
|
|
53
|
+
const visited = new Set<string>();
|
|
54
|
+
let current =
|
|
55
|
+
klass.parent === undefined ? undefined : { name: klass.parent, defaultNamespace: context.namespace.name };
|
|
56
|
+
while (current !== undefined) {
|
|
57
|
+
const resolved = resolveQualifiedClass(context.repository, current.name, current.defaultNamespace);
|
|
58
|
+
if (resolved === undefined) break;
|
|
59
|
+
const key = `${resolved.namespaceName}.${resolved.klass.name}`;
|
|
60
|
+
if (visited.has(key)) break;
|
|
61
|
+
visited.add(key);
|
|
62
|
+
visit(resolved);
|
|
63
|
+
current = resolved.klass.parent
|
|
64
|
+
? { name: resolved.klass.parent, defaultNamespace: resolved.namespaceName }
|
|
65
|
+
: undefined;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Collects the properties contributed by a class's directly-implemented
|
|
71
|
+
* interfaces that are not already declared on the class itself or inherited
|
|
72
|
+
* from an ancestor (or an ancestor-implemented interface).
|
|
73
|
+
*
|
|
74
|
+
* Each returned property carries its references re-rooted to the interface's
|
|
75
|
+
* namespace so the class writer can emit its accessor and constructor prop
|
|
76
|
+
* as if the property had been authored on the class.
|
|
77
|
+
*
|
|
78
|
+
* @param context - The module context
|
|
79
|
+
* @param klass - The implementing class
|
|
80
|
+
*/
|
|
81
|
+
export const collectInterfaceProperties = (context: ModuleContext, klass: GirClass): readonly GirProperty[] => {
|
|
82
|
+
const seen = new Set<string>();
|
|
83
|
+
for (const property of klass.properties) seen.add(toIdentifier(toCamelCase(property.name)));
|
|
84
|
+
forEachAncestor(context, klass, (ancestor) => {
|
|
85
|
+
for (const property of ancestor.klass.properties) seen.add(toIdentifier(toCamelCase(property.name)));
|
|
86
|
+
for (const implementName of ancestor.klass.implements) {
|
|
87
|
+
const iface = resolveImplementedInterface(context, implementName, ancestor.namespaceName);
|
|
88
|
+
if (iface === undefined) continue;
|
|
89
|
+
for (const property of iface.klass.properties) seen.add(toIdentifier(toCamelCase(property.name)));
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const result: GirProperty[] = [];
|
|
93
|
+
for (const implementName of klass.implements) {
|
|
94
|
+
const iface = resolveImplementedInterface(context, implementName);
|
|
95
|
+
if (iface === undefined) continue;
|
|
96
|
+
for (const property of iface.klass.properties) {
|
|
97
|
+
const name = toIdentifier(toCamelCase(property.name));
|
|
98
|
+
if (seen.has(name)) continue;
|
|
99
|
+
seen.add(name);
|
|
100
|
+
result.push({ ...property, type: qualifyTypeRef(property.type, iface.namespaceName) });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { toPascalCase } from "@gtkx/utils";
|
|
2
|
+
import type { ModuleContext } from "../dsl/context.js";
|
|
3
|
+
import { indent } from "../dsl/emit.js";
|
|
4
|
+
import type { GirClass } from "../gir/class.js";
|
|
5
|
+
import type { GirFunction } from "../gir/function.js";
|
|
6
|
+
import { splitQualifiedName } from "../gir/qualified-name.js";
|
|
7
|
+
import { qualifyFunction } from "../gir/qualify.js";
|
|
8
|
+
import {
|
|
9
|
+
appendMethodBinding,
|
|
10
|
+
type Callables,
|
|
11
|
+
dedupeCallables,
|
|
12
|
+
emitBindings,
|
|
13
|
+
indexMethodsByName,
|
|
14
|
+
renderInstanceMethod,
|
|
15
|
+
renderPlainTypeMembers,
|
|
16
|
+
} from "./callables.js";
|
|
17
|
+
import { renderVfuncMetadata } from "./class-struct.js";
|
|
18
|
+
import { renderGetTypeReference } from "./gtype-binding.js";
|
|
19
|
+
import { resolveImplementedInterface } from "./inheritance.js";
|
|
20
|
+
import { methodExportName } from "./method.js";
|
|
21
|
+
import { renderPropertyAccessor } from "./property-accessor.js";
|
|
22
|
+
import { appendNativeClassRegistration } from "./registration.js";
|
|
23
|
+
import { renderSignalMembers } from "./signal.js";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Emits a class declaration for a `<interface>` element.
|
|
27
|
+
*
|
|
28
|
+
* Interfaces have no GIR parent — they extend the GObject base
|
|
29
|
+
* (`GObject.Object`) in the generated output because every implementor is
|
|
30
|
+
* itself a GObject. Methods, static functions, and constructors are
|
|
31
|
+
* surfaced on the interface class just like a regular class so JS callers
|
|
32
|
+
* can dispatch them directly on interface-typed values.
|
|
33
|
+
*
|
|
34
|
+
* @param context - The module context
|
|
35
|
+
* @param iface - The interface to emit
|
|
36
|
+
*/
|
|
37
|
+
export const emitInterface = (context: ModuleContext, iface: GirClass): void => {
|
|
38
|
+
if (!iface.introspectable) return;
|
|
39
|
+
if (iface.name.length === 0) return;
|
|
40
|
+
const className = toPascalCase(iface.name);
|
|
41
|
+
const callables: Callables = {
|
|
42
|
+
constructors: dedupeCallables(iface.constructors),
|
|
43
|
+
functions: dedupeCallables(iface.functions),
|
|
44
|
+
methods: dedupeCallables(iface.methods),
|
|
45
|
+
};
|
|
46
|
+
emitBindings(context, callables);
|
|
47
|
+
|
|
48
|
+
const parent = resolveInterfaceParent(context);
|
|
49
|
+
const members = renderInterfaceMembers(context, iface, callables);
|
|
50
|
+
const body = members.map((member) => indent(member, 1)).join("\n\n");
|
|
51
|
+
context.module.appendDeclaration(`export class ${className} extends ${parent} {\n${body}\n}`);
|
|
52
|
+
|
|
53
|
+
const prerequisiteRefs = iface.prerequisites
|
|
54
|
+
.map((name) => resolvePrerequisiteReference(context, name))
|
|
55
|
+
.filter((entry): entry is string => entry !== undefined);
|
|
56
|
+
if (prerequisiteRefs.length > 0) {
|
|
57
|
+
context.module.appendDeclaration(`export interface ${className} extends ${prerequisiteRefs.join(", ")} {}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
appendInterfaceRegistrations(context, iface, className);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const renderInterfaceMembers = (context: ModuleContext, iface: GirClass, callables: Callables): readonly string[] => {
|
|
64
|
+
const className = toPascalCase(iface.name);
|
|
65
|
+
const { members, claimedNames } = renderPlainTypeMembers({
|
|
66
|
+
context,
|
|
67
|
+
className,
|
|
68
|
+
callables,
|
|
69
|
+
hasGType: true,
|
|
70
|
+
wrap: "interface",
|
|
71
|
+
});
|
|
72
|
+
appendPrerequisiteMethods(context, iface, members, claimedNames);
|
|
73
|
+
const methodByName = indexMethodsByName(callables.methods);
|
|
74
|
+
for (const property of iface.properties) {
|
|
75
|
+
const block = renderPropertyAccessor(context, property, claimedNames, methodByName);
|
|
76
|
+
if (block !== undefined) members.push(block);
|
|
77
|
+
}
|
|
78
|
+
members.push(...renderSignalMembers(context, iface));
|
|
79
|
+
return members;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Copies the methods of an interface's transitive prerequisite interfaces onto
|
|
84
|
+
* the interface wrapper class.
|
|
85
|
+
*
|
|
86
|
+
* A `Gtk.SelectionModel` value is also a `Gio.ListModel`, but the JS wrapper
|
|
87
|
+
* only exposes `getNItems`/`getItem` if those prerequisite methods are emitted
|
|
88
|
+
* on the wrapper. Each prerequisite interface's methods are re-rooted to its
|
|
89
|
+
* namespace, bound, and emitted as direct members; names already provided by
|
|
90
|
+
* the interface itself are skipped.
|
|
91
|
+
*
|
|
92
|
+
* @param context - The module context
|
|
93
|
+
* @param iface - The interface being emitted
|
|
94
|
+
* @param members - The accumulating member list
|
|
95
|
+
* @param claimedNames - Names already emitted on the interface body
|
|
96
|
+
*/
|
|
97
|
+
const appendPrerequisiteMethods = (
|
|
98
|
+
context: ModuleContext,
|
|
99
|
+
iface: GirClass,
|
|
100
|
+
members: string[],
|
|
101
|
+
claimedNames: Set<string>,
|
|
102
|
+
): void => {
|
|
103
|
+
for (const method of collectPrerequisiteMethods(context, iface)) {
|
|
104
|
+
const name = methodExportName(method);
|
|
105
|
+
if (name === "constructor" || claimedNames.has(name)) continue;
|
|
106
|
+
const block = renderInstanceMethod(context, method);
|
|
107
|
+
if (block === undefined) continue;
|
|
108
|
+
appendMethodBinding(context, method);
|
|
109
|
+
members.push(block);
|
|
110
|
+
claimedNames.add(name);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const collectPrerequisiteMethods = (context: ModuleContext, iface: GirClass): readonly GirFunction[] => {
|
|
115
|
+
const result: GirFunction[] = [];
|
|
116
|
+
const visited = new Set<string>();
|
|
117
|
+
const seen = new Set<string>();
|
|
118
|
+
for (const method of iface.methods) {
|
|
119
|
+
if (method.introspectable) seen.add(methodExportName(method));
|
|
120
|
+
}
|
|
121
|
+
const visit = (klass: GirClass, namespaceName: string): void => {
|
|
122
|
+
for (const prerequisiteName of klass.prerequisites) {
|
|
123
|
+
const prerequisite = resolveImplementedInterface(context, prerequisiteName, namespaceName);
|
|
124
|
+
if (prerequisite === undefined) continue;
|
|
125
|
+
const key = `${prerequisite.namespaceName}.${prerequisite.klass.name}`;
|
|
126
|
+
if (visited.has(key)) continue;
|
|
127
|
+
visited.add(key);
|
|
128
|
+
for (const method of dedupeCallables(prerequisite.klass.methods)) {
|
|
129
|
+
const name = methodExportName(method);
|
|
130
|
+
if (seen.has(name)) continue;
|
|
131
|
+
seen.add(name);
|
|
132
|
+
result.push(qualifyFunction(method, prerequisite.namespaceName));
|
|
133
|
+
}
|
|
134
|
+
visit(prerequisite.klass, prerequisite.namespaceName);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
visit(iface, context.namespace.name);
|
|
138
|
+
return result;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const appendInterfaceRegistrations = (context: ModuleContext, iface: GirClass, className: string): void => {
|
|
142
|
+
const getTypeRef =
|
|
143
|
+
iface.glibGetType === undefined
|
|
144
|
+
? undefined
|
|
145
|
+
: renderGetTypeReference(context, iface.glibGetType, iface.glibTypeName);
|
|
146
|
+
appendNativeClassRegistration(context, {
|
|
147
|
+
className,
|
|
148
|
+
role: "interface",
|
|
149
|
+
getTypeRef,
|
|
150
|
+
vfuncs: renderVfuncMetadata(context, iface),
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const resolveInterfaceParent = (context: ModuleContext): string => {
|
|
155
|
+
if (context.namespace.name === "GObject") return "Object";
|
|
156
|
+
const alias = context.addCrossNamespaceImport("GObject");
|
|
157
|
+
return `${alias}.Object`;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const resolvePrerequisiteReference = (context: ModuleContext, name: string): string | undefined => {
|
|
161
|
+
const { namespaceName, typeName } = splitQualifiedName(name, context.namespace.name);
|
|
162
|
+
const resolved = context.repository.resolveNamed(namespaceName, typeName);
|
|
163
|
+
if (resolved === undefined) return undefined;
|
|
164
|
+
if (resolved.kind !== "interface" && resolved.kind !== "class") return undefined;
|
|
165
|
+
if (namespaceName === context.namespace.name) return toPascalCase(typeName);
|
|
166
|
+
const alias = context.addCrossNamespaceImport(namespaceName);
|
|
167
|
+
return `${alias}.${toPascalCase(typeName)}`;
|
|
168
|
+
};
|