@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,318 @@
|
|
|
1
|
+
import { quote, toCamelCase } 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 { GirParameter } from "../gir/parameter.js";
|
|
6
|
+
import { isOutParameter } from "../gir/parameter.js";
|
|
7
|
+
import { qualifyTypeRef } from "../gir/qualify.js";
|
|
8
|
+
import type { GirSignal } from "../gir/signal.js";
|
|
9
|
+
import type { GirTypeRef, PrimitiveTypeRef } from "../gir/type-ref.js";
|
|
10
|
+
import { forEachAncestor, resolveImplementedInterface } from "./inheritance.js";
|
|
11
|
+
import { planTrampolineArgs, renderTupleWriteback } from "./method.js";
|
|
12
|
+
import { isHandlePassing } from "./param-classify.js";
|
|
13
|
+
import { isCellInout, renderFfiType } from "./value.js";
|
|
14
|
+
|
|
15
|
+
const SIGNAL_HANDLER_TYPE = "(...args: any[]) => any";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A signal together with the namespace its parameter and return references
|
|
19
|
+
* belong to. Own signals carry the class's namespace; signals flattened from
|
|
20
|
+
* an implemented interface carry the interface's namespace so their references
|
|
21
|
+
* resolve correctly.
|
|
22
|
+
*/
|
|
23
|
+
type CollectedSignal = {
|
|
24
|
+
readonly signal: GirSignal;
|
|
25
|
+
readonly namespaceName: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** The generated marshalling references a class's inline `emit` switch uses. */
|
|
29
|
+
type GObjectRefs = {
|
|
30
|
+
readonly Value: string;
|
|
31
|
+
readonly signalEmitv: string;
|
|
32
|
+
readonly signalLookup: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Renders the `connect` and `emit` instance methods for a class that owns or
|
|
37
|
+
* inherits-by-interface at least one signal.
|
|
38
|
+
*
|
|
39
|
+
* Each method is a `switch` over the type's own signals. `connect` resolves the
|
|
40
|
+
* per-signal trampoline, wraps the handler, and hands it to the thin
|
|
41
|
+
* `connectSignal` wrapper around the non-introspectable `g_signal_connect_data`.
|
|
42
|
+
* `emit` marshals the arguments into `GValue`s with `valueFromFfi` and calls the
|
|
43
|
+
* generated `signalLookup` / `signalEmitv` directly. Unknown signals route up
|
|
44
|
+
* the class chain via `super.connect` / `super.emit`; the root `GObject.Object`
|
|
45
|
+
* throws because it bottoms out the hierarchy.
|
|
46
|
+
*
|
|
47
|
+
* Returns an empty array when the class contributes no signals of its own.
|
|
48
|
+
*
|
|
49
|
+
* @param context - The module context
|
|
50
|
+
* @param klass - The class whose signal methods to render
|
|
51
|
+
*/
|
|
52
|
+
export const renderSignalMembers = (context: ModuleContext, klass: GirClass): readonly string[] => {
|
|
53
|
+
if (klass.glibGetType === undefined) return [];
|
|
54
|
+
const signals = collectClassSignals(context, klass);
|
|
55
|
+
if (signals.length === 0) return [];
|
|
56
|
+
const isRootObject = context.namespace.name === "GObject" && klass.name === "Object";
|
|
57
|
+
|
|
58
|
+
context.addRuntimeImport("connectSignal");
|
|
59
|
+
context.addRuntimeImport("signalBaseName");
|
|
60
|
+
context.addRuntimeImport("t");
|
|
61
|
+
|
|
62
|
+
const connectCases = signals.map((collected) => renderConnectCase(context, collected));
|
|
63
|
+
const emitCases = signals.map((collected) => renderEmitCase(context, collected));
|
|
64
|
+
const connectDefault = isRootObject
|
|
65
|
+
? `default:\n throw new globalThis.Error("Unknown signal '" + signal + "'");`
|
|
66
|
+
: "default:\n return super.connect(signal, handler, after);";
|
|
67
|
+
const emitDefault = isRootObject
|
|
68
|
+
? `default:\n throw new globalThis.Error("Unknown signal '" + sigName + "'");`
|
|
69
|
+
: "default:\n super.emit(sigName, ...args);";
|
|
70
|
+
|
|
71
|
+
const connectSwitch = `switch (signalBaseName(signal)) {\n${indent([...connectCases, connectDefault].join("\n"), 1)}\n}`;
|
|
72
|
+
const emitSwitch = `switch (sigName) {\n${indent([...emitCases, emitDefault].join("\n"), 1)}\n}`;
|
|
73
|
+
|
|
74
|
+
return [
|
|
75
|
+
`connect(signal: string, handler: ${SIGNAL_HANDLER_TYPE}, after?: boolean): number {\n${indent(connectSwitch, 1)}\n}`,
|
|
76
|
+
`emit(sigName: string, ...args: unknown[]): void {\n${indent(emitSwitch, 1)}\n}`,
|
|
77
|
+
];
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Renders one `connect` switch case: it resolves the signal's typed trampoline,
|
|
82
|
+
* wraps the user handler with the per-signal in-parameter marshalling closure,
|
|
83
|
+
* and dispatches `g_signal_connect_data` through {@link connectSignal} with the
|
|
84
|
+
* full detailed signal name.
|
|
85
|
+
*/
|
|
86
|
+
const renderConnectCase = (context: ModuleContext, collected: CollectedSignal): string => {
|
|
87
|
+
const { signal } = collected;
|
|
88
|
+
const { trampoline, invoke } = renderTrampolineAndInvoke(context, collected);
|
|
89
|
+
const body = [
|
|
90
|
+
`const invoke = ${invoke};`,
|
|
91
|
+
"const handlerWrapper = (...args: unknown[]): unknown => invoke(handler, args);",
|
|
92
|
+
`return connectSignal(this, signal, ${trampoline}, handlerWrapper, after ?? false);`,
|
|
93
|
+
].join("\n");
|
|
94
|
+
return `case ${quote(signal.name)}: {\n${indent(body, 1)}\n}`;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Renders one `emit` switch case: it marshals the emitting instance and each
|
|
99
|
+
* signal parameter into a `GValue` with `valueFromFfi`, resolves the signal id
|
|
100
|
+
* from the instance's runtime `GType`, and dispatches `g_signal_emitv`. Signals
|
|
101
|
+
* with a non-void return supply an initialized return `GValue`.
|
|
102
|
+
*/
|
|
103
|
+
const renderEmitCase = (context: ModuleContext, collected: CollectedSignal): string => {
|
|
104
|
+
const { signal, namespaceName } = collected;
|
|
105
|
+
const params = signal.parameters.filter((parameter) => !parameter.isVarargs);
|
|
106
|
+
const paramFfi = params.map((parameter) =>
|
|
107
|
+
renderFfiType(context, qualifyTypeRef(parameter.type, namespaceName), parameter.transferOwnership),
|
|
108
|
+
);
|
|
109
|
+
context.addValueFromFfiImport();
|
|
110
|
+
const refs = gobjectRefs(context);
|
|
111
|
+
const valueArgs = [
|
|
112
|
+
'valueFromFfi(t.object("full"), this)',
|
|
113
|
+
...paramFfi.map((ffi, index) => `valueFromFfi(${ffi}, args[${index}])`),
|
|
114
|
+
];
|
|
115
|
+
const values = `[${valueArgs.join(", ")}]`;
|
|
116
|
+
const lookup = `${refs.signalLookup}(${quote(signal.name)}, this.__gtype__)`;
|
|
117
|
+
const returnRef = qualifyTypeRef(signal.returnValue.type, namespaceName);
|
|
118
|
+
const emitStatements =
|
|
119
|
+
returnRef === undefined || isVoidRef(returnRef)
|
|
120
|
+
? `${refs.signalEmitv}(${values}, ${lookup}, 0);`
|
|
121
|
+
: [
|
|
122
|
+
`const returnValue = new ${refs.Value}();`,
|
|
123
|
+
`returnValue.init(${renderReturnGType(context, returnRef)});`,
|
|
124
|
+
`${refs.signalEmitv}(${values}, ${lookup}, 0, returnValue);`,
|
|
125
|
+
].join("\n");
|
|
126
|
+
return `case ${quote(signal.name)}: {\n${indent(`${emitStatements}\nreturn;`, 1)}\n}`;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Resolves the `GObject` marshalling references the inline `emit` switch needs.
|
|
131
|
+
* Within the `GObject` namespace the generated `Value`, `signalEmitv`, and
|
|
132
|
+
* `signalLookup` are local; elsewhere they are reached through the cross-
|
|
133
|
+
* namespace `GObject` alias.
|
|
134
|
+
*/
|
|
135
|
+
const gobjectRefs = (context: ModuleContext): GObjectRefs => {
|
|
136
|
+
if (context.namespace.name === "GObject") {
|
|
137
|
+
return { Value: "Value", signalEmitv: "signalEmitv", signalLookup: "signalLookup" };
|
|
138
|
+
}
|
|
139
|
+
const alias = context.addCrossNamespaceImport("GObject");
|
|
140
|
+
return { Value: `${alias}.Value`, signalEmitv: `${alias}.signalEmitv`, signalLookup: `${alias}.signalLookup` };
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Renders the trampoline FFI descriptor used to connect a handler and the
|
|
145
|
+
* `invoke` closure that marshals the trampoline arguments into the user handler.
|
|
146
|
+
*
|
|
147
|
+
* Out-parameter cells are wrapped in `t.ref(...)` so the native trampoline can
|
|
148
|
+
* write them back; the `invoke` closure mirrors the out-parameter tuple
|
|
149
|
+
* convention used by method out-parameters.
|
|
150
|
+
*/
|
|
151
|
+
const renderTrampolineAndInvoke = (
|
|
152
|
+
context: ModuleContext,
|
|
153
|
+
collected: CollectedSignal,
|
|
154
|
+
): { readonly trampoline: string; readonly invoke: string } => {
|
|
155
|
+
const { signal, namespaceName } = collected;
|
|
156
|
+
const params = signal.parameters.filter((parameter) => !parameter.isVarargs);
|
|
157
|
+
const paramFfi = params.map((parameter) =>
|
|
158
|
+
renderFfiType(context, qualifyTypeRef(parameter.type, namespaceName), parameter.transferOwnership),
|
|
159
|
+
);
|
|
160
|
+
const trampolineParamFfi = params.map((parameter, index) =>
|
|
161
|
+
isOutParameter(parameter) || isCellInout(context, parameter) ? `t.ref(${paramFfi[index]})` : paramFfi[index],
|
|
162
|
+
);
|
|
163
|
+
const returnRef = qualifyTypeRef(signal.returnValue.type, namespaceName);
|
|
164
|
+
const isVoid = isVoidRef(returnRef);
|
|
165
|
+
const returnFfi = isVoid ? "t.void" : renderFfiType(context, returnRef, signal.returnValue.transferOwnership);
|
|
166
|
+
const trampolineArgs = ['t.object("borrowed")', ...trampolineParamFfi, "t.void"].join(", ");
|
|
167
|
+
const trampoline = `t.trampoline([${trampolineArgs}], ${returnFfi}, { hasDestroy: true, userDataIndex: ${params.length + 1} })`;
|
|
168
|
+
const invoke = renderInvokeClosure(context, collected, params, isVoid ? undefined : returnRef);
|
|
169
|
+
return { trampoline, invoke };
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const collectClassSignals = (context: ModuleContext, klass: GirClass): readonly CollectedSignal[] => {
|
|
173
|
+
const inheritedNames = collectInheritedSignalNames(context, klass);
|
|
174
|
+
const seen = new Set<string>();
|
|
175
|
+
const result: CollectedSignal[] = [];
|
|
176
|
+
for (const signal of klass.signals) {
|
|
177
|
+
const name = toCamelCase(signal.name);
|
|
178
|
+
if (inheritedNames.has(name) || seen.has(name)) continue;
|
|
179
|
+
seen.add(name);
|
|
180
|
+
result.push({ signal, namespaceName: context.namespace.name });
|
|
181
|
+
}
|
|
182
|
+
for (const implementName of klass.implements) {
|
|
183
|
+
const iface = resolveImplementedInterface(context, implementName);
|
|
184
|
+
if (iface === undefined) continue;
|
|
185
|
+
for (const signal of iface.klass.signals) {
|
|
186
|
+
const name = toCamelCase(signal.name);
|
|
187
|
+
if (inheritedNames.has(name) || seen.has(name)) continue;
|
|
188
|
+
seen.add(name);
|
|
189
|
+
result.push({ signal, namespaceName: iface.namespaceName });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return result;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const collectInheritedSignalNames = (context: ModuleContext, klass: GirClass): ReadonlySet<string> => {
|
|
196
|
+
const names = new Set<string>();
|
|
197
|
+
forEachAncestor(context, klass, (ancestor) => {
|
|
198
|
+
for (const signal of ancestor.klass.signals) names.add(toCamelCase(signal.name));
|
|
199
|
+
for (const implementName of ancestor.klass.implements) {
|
|
200
|
+
const iface = resolveImplementedInterface(context, implementName, ancestor.namespaceName);
|
|
201
|
+
if (iface === undefined) continue;
|
|
202
|
+
for (const signal of iface.klass.signals) names.add(toCamelCase(signal.name));
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
return names;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const renderInvokeClosure = (
|
|
209
|
+
context: ModuleContext,
|
|
210
|
+
collected: CollectedSignal,
|
|
211
|
+
params: readonly GirParameter[],
|
|
212
|
+
returnRef: GirTypeRef | undefined,
|
|
213
|
+
): string => {
|
|
214
|
+
const { namespaceName } = collected;
|
|
215
|
+
const { callArgs, outArgIndices } = planTrampolineArgs(context, params, namespaceName, 1);
|
|
216
|
+
if (outArgIndices.length === 0) {
|
|
217
|
+
if (returnRef !== undefined && isHandlePassing(context, returnRef)) {
|
|
218
|
+
context.addRuntimeImport("tryGetHandle");
|
|
219
|
+
return `(handler, args) => {\n const _result = handler(${callArgs});\n return tryGetHandle(_result);\n}`;
|
|
220
|
+
}
|
|
221
|
+
return `(handler, args) => handler(${callArgs})`;
|
|
222
|
+
}
|
|
223
|
+
return renderOutParamInvoke(context, callArgs, outArgIndices, returnRef);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Renders the invoke closure for a signal with out-parameters.
|
|
228
|
+
*
|
|
229
|
+
* The handler returns its results as the tuple {@link renderMethodReturnType}
|
|
230
|
+
* describes (`[primary, ...outs]` when both exist, the scalar out alone for a
|
|
231
|
+
* void return with a single out, or an out-only tuple otherwise). The shared
|
|
232
|
+
* {@link renderTupleWriteback} destructures that tuple and writes each out
|
|
233
|
+
* value into its trampoline cell's `value` slot — the native side flushes
|
|
234
|
+
* those cells through the matching C out-pointers, so the tuple convention
|
|
235
|
+
* stays entirely in generated code.
|
|
236
|
+
*
|
|
237
|
+
* @param context - The module context
|
|
238
|
+
* @param callArgs - The rendered in-parameter call arguments
|
|
239
|
+
* @param outArgIndices - Trampoline-arg indices of the out-parameter cells
|
|
240
|
+
* @param returnRef - The signal's return type, or `undefined` for void
|
|
241
|
+
*/
|
|
242
|
+
const renderOutParamInvoke = (
|
|
243
|
+
context: ModuleContext,
|
|
244
|
+
callArgs: string,
|
|
245
|
+
outArgIndices: readonly number[],
|
|
246
|
+
returnRef: GirTypeRef | undefined,
|
|
247
|
+
): string => {
|
|
248
|
+
const body = renderTupleWriteback(context, `handler(${callArgs})`, outArgIndices, returnRef);
|
|
249
|
+
return `(handler, args) => {\n ${body}\n}`;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const renderReturnGType = (context: ModuleContext, ref: GirTypeRef): string => {
|
|
253
|
+
if (ref.kind === "primitive") {
|
|
254
|
+
return `${typeFromNameReference(context)}(${quote(primitiveGTypeName(ref.category))})`;
|
|
255
|
+
}
|
|
256
|
+
if (ref.kind === "named") {
|
|
257
|
+
const owner = ref.namespaceName ?? context.namespace.name;
|
|
258
|
+
const resolved = context.repository.resolveNamed(owner, ref.typeName);
|
|
259
|
+
if (resolved !== undefined && resolved.kind === "enum") {
|
|
260
|
+
context.addNativeImport("call");
|
|
261
|
+
context.addRuntimeImport("t");
|
|
262
|
+
const lib = resolved.namespace.sharedLibrary ?? "";
|
|
263
|
+
const getter = resolved.value.glibGetType ?? "";
|
|
264
|
+
return `call(${quote(lib)}, ${quote(getter)}, [], t.uint64)`;
|
|
265
|
+
}
|
|
266
|
+
if (
|
|
267
|
+
resolved !== undefined &&
|
|
268
|
+
(resolved.kind === "class" || resolved.kind === "interface" || resolved.kind === "boxed")
|
|
269
|
+
) {
|
|
270
|
+
const glibTypeName = glibTypeNameOf(resolved.value) ?? ref.typeName;
|
|
271
|
+
return `${typeFromNameReference(context)}(${quote(glibTypeName)})`;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return `${typeFromNameReference(context)}("GObject")`;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const isVoidRef = (ref: GirTypeRef | undefined): boolean =>
|
|
278
|
+
ref === undefined || (ref.kind === "primitive" && ref.category === "void");
|
|
279
|
+
|
|
280
|
+
const glibTypeNameOf = (value: {
|
|
281
|
+
readonly glibTypeName?: string | undefined;
|
|
282
|
+
readonly cType?: string | undefined;
|
|
283
|
+
}): string | undefined => value.glibTypeName ?? value.cType;
|
|
284
|
+
|
|
285
|
+
const typeFromNameReference = (context: ModuleContext): string => {
|
|
286
|
+
if (context.namespace.name === "GObject") return "typeFromName";
|
|
287
|
+
const alias = context.addCrossNamespaceImport("GObject");
|
|
288
|
+
return `${alias}.typeFromName`;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const primitiveGTypeName = (category: PrimitiveTypeRef["category"]): string => {
|
|
292
|
+
switch (category) {
|
|
293
|
+
case "boolean":
|
|
294
|
+
return "gboolean";
|
|
295
|
+
case "int8":
|
|
296
|
+
case "int16":
|
|
297
|
+
case "int32":
|
|
298
|
+
return "gint";
|
|
299
|
+
case "uint8":
|
|
300
|
+
case "uint16":
|
|
301
|
+
case "uint32":
|
|
302
|
+
case "unichar":
|
|
303
|
+
return "guint";
|
|
304
|
+
case "int64":
|
|
305
|
+
return "gint64";
|
|
306
|
+
case "uint64":
|
|
307
|
+
case "pointer":
|
|
308
|
+
return "guint64";
|
|
309
|
+
case "float32":
|
|
310
|
+
return "gfloat";
|
|
311
|
+
case "float64":
|
|
312
|
+
return "gdouble";
|
|
313
|
+
case "string":
|
|
314
|
+
return "gchararray";
|
|
315
|
+
case "void":
|
|
316
|
+
return "void";
|
|
317
|
+
}
|
|
318
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { ModuleContext } from "../dsl/context.js";
|
|
2
|
+
import { aliasExportName } from "../dsl/identifier.js";
|
|
3
|
+
import { PRIMITIVE_TS_TYPE } from "../gir/primitives.js";
|
|
4
|
+
import type { ResolvedNamed } from "../gir/repository.js";
|
|
5
|
+
import type { GirTypeRef, NamedTypeRef } from "../gir/type-ref.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* TypeScript type annotations the writers emit alongside runtime values.
|
|
9
|
+
*
|
|
10
|
+
* Generated `.ts` files are consumed both as runtime modules (transpiled to
|
|
11
|
+
* `.js`) and as declaration sources (`ts.transpileDeclaration` extracts the
|
|
12
|
+
* `.d.ts` shape). Every exported value, parameter, and return position
|
|
13
|
+
* therefore carries an explicit annotation so isolated declaration emit
|
|
14
|
+
* succeeds without inference. The helpers below produce those annotations
|
|
15
|
+
* from the same `GirTypeRef` shapes the runtime FFI writers consume,
|
|
16
|
+
* importing referenced cross-namespace classes on demand.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Strategy describing the type-ref shapes the FFI and React generators render
|
|
21
|
+
* differently: container style (`Map` vs `Record`), the callback placeholder,
|
|
22
|
+
* whether a `GByteArray` collapses to `number[]`, and how a named reference is
|
|
23
|
+
* qualified and imported. {@link renderBaseTypeFor} renders every other shape
|
|
24
|
+
* identically for both.
|
|
25
|
+
*/
|
|
26
|
+
export type TsTypeTarget = {
|
|
27
|
+
/** `"map"` → `Map<K, V>`; `"record"` → `Record<K, V>`. */
|
|
28
|
+
readonly containerStyle: "map" | "record";
|
|
29
|
+
/** The placeholder emitted for a callback type. */
|
|
30
|
+
readonly callbackType: string;
|
|
31
|
+
/** Whether a `GByteArray` renders as `number[]` instead of `element[]`. */
|
|
32
|
+
readonly byteArrayAsNumber: boolean;
|
|
33
|
+
/** Renders a named reference (class/interface/boxed/enum/callback/alias). */
|
|
34
|
+
readonly renderNamed: (ref: NamedTypeRef) => string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Renders the TypeScript annotation for a `GirTypeRef`, dispatching the shapes
|
|
39
|
+
* both generators share and delegating the divergent ones to {@link target}.
|
|
40
|
+
*
|
|
41
|
+
* @param target - The per-generator rendering strategy
|
|
42
|
+
* @param ref - The type reference, or `undefined` for void
|
|
43
|
+
*/
|
|
44
|
+
export const renderBaseTypeFor = (target: TsTypeTarget, ref: GirTypeRef | undefined): string => {
|
|
45
|
+
if (ref === undefined) return "void";
|
|
46
|
+
switch (ref.kind) {
|
|
47
|
+
case "primitive":
|
|
48
|
+
return PRIMITIVE_TS_TYPE[ref.category];
|
|
49
|
+
case "varargs":
|
|
50
|
+
return "unknown[]";
|
|
51
|
+
case "callback":
|
|
52
|
+
return target.callbackType;
|
|
53
|
+
case "named":
|
|
54
|
+
return target.renderNamed(ref);
|
|
55
|
+
case "array":
|
|
56
|
+
return `${renderBaseTypeFor(target, ref.element)}[]`;
|
|
57
|
+
case "list":
|
|
58
|
+
if (ref.flavor === "gbytearray" && target.byteArrayAsNumber) return "number[]";
|
|
59
|
+
return `${renderBaseTypeFor(target, ref.element)}[]`;
|
|
60
|
+
case "hashtable": {
|
|
61
|
+
const key = renderBaseTypeFor(target, ref.key);
|
|
62
|
+
const value = renderBaseTypeFor(target, ref.value);
|
|
63
|
+
return target.containerStyle === "record" ? `Record<${key}, ${value}>` : `Map<${key}, ${value}>`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const moduleTarget = (context: ModuleContext): TsTypeTarget => ({
|
|
69
|
+
containerStyle: "map",
|
|
70
|
+
callbackType: "((...args: any[]) => any)",
|
|
71
|
+
byteArrayAsNumber: true,
|
|
72
|
+
renderNamed: (ref) => namedTsType(context, ref),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Renders a TypeScript type annotation for a `GirTypeRef` in an FFI module:
|
|
77
|
+
* classes/interfaces/boxeds/enums/callbacks as their qualified identifier,
|
|
78
|
+
* arrays and lists as `T[]`, hashtables as `Map<K, V>`, primitives to their TS
|
|
79
|
+
* counterpart, and `undefined` to `void`.
|
|
80
|
+
*
|
|
81
|
+
* @param context - The module context
|
|
82
|
+
* @param ref - The type reference, or `undefined` for void
|
|
83
|
+
* @param isNullable - Whether the slot accepts `null`
|
|
84
|
+
*/
|
|
85
|
+
export const renderTsType = (context: ModuleContext, ref: GirTypeRef | undefined, isNullable = false): string => {
|
|
86
|
+
const base = renderBaseTypeFor(moduleTarget(context), ref);
|
|
87
|
+
return isNullable ? `${base} | null` : base;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const namedTsType = (context: ModuleContext, ref: NamedTypeRef): string => {
|
|
91
|
+
const namespaceName = ref.namespaceName ?? context.namespace.name;
|
|
92
|
+
const resolved = context.repository.resolveNamed(namespaceName, ref.typeName);
|
|
93
|
+
if (resolved === undefined) {
|
|
94
|
+
return context.qualify(namespaceName, ref.typeName);
|
|
95
|
+
}
|
|
96
|
+
return resolvedTsType(context, namespaceName, ref.typeName, resolved);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const resolvedTsType = (
|
|
100
|
+
context: ModuleContext,
|
|
101
|
+
namespaceName: string,
|
|
102
|
+
typeName: string,
|
|
103
|
+
resolved: ResolvedNamed,
|
|
104
|
+
): string => {
|
|
105
|
+
switch (resolved.kind) {
|
|
106
|
+
case "class":
|
|
107
|
+
case "interface":
|
|
108
|
+
case "boxed":
|
|
109
|
+
case "enum":
|
|
110
|
+
case "callback":
|
|
111
|
+
return context.qualify(namespaceName, typeName);
|
|
112
|
+
case "alias":
|
|
113
|
+
return context.qualify(namespaceName, aliasExportName(namespaceName, typeName));
|
|
114
|
+
}
|
|
115
|
+
};
|