@ts-for-gir/lib
Version:
Typescript .d.ts generator from GIR for gjs
683 lines • 25.9 kB
JavaScript
import { TypeIdentifier, UnknownType, VoidType, ArrayType, ClosureType, NullableType, FunctionType } from "../gir.js";
import { IntrospectedBase, IntrospectedClassMember, IntrospectedNamespaceMember } from "./base.js";
import { GirDirection } from "../index.js";
import { isIntrospectable } from "./namespace.js";
import { getType, isInvalid, sanitizeMemberName, sanitizeIdentifierName, parseDoc, parseMetadata } from "./util.js";
import { IntrospectedField } from "./property.js";
import { IntrospectedBaseClass } from "./nodes.js";
function hasShadow(obj) {
return obj.$["shadows"] != null;
}
export class IntrospectedFunction extends IntrospectedNamespaceMember {
parameters;
output_parameters;
return_type;
raw_name;
generics = [];
returnTypeDoc;
constructor({ name, raw_name, return_type = UnknownType, parameters = [], output_parameters = [], namespace, ...args }) {
super(name, namespace, { ...args });
this.raw_name = raw_name;
this.parameters = parameters.map(p => p.copy({ parent: this }));
this.output_parameters = output_parameters.map(p => p.copy({ parent: this }));
this.return_type = return_type;
this.returnTypeDoc = null;
}
copy({ parent = this.parent, outputParameters, parameters, return_type } = { parent: this.parent }) {
const fn = new IntrospectedFunction({
namespace: parent ?? this.namespace,
raw_name: this.raw_name,
name: this.name,
return_type: return_type ?? this.return_type,
output_parameters: outputParameters ?? this.output_parameters,
parameters: parameters ?? this.parameters
});
fn.returnTypeDoc = this.returnTypeDoc;
fn.generics = [...this.generics];
return fn._copyBaseProperties(this);
}
accept(visitor) {
const node = this.copy({
parent: this.parent,
parameters: this.parameters.map(p => {
return p.accept(visitor);
}),
outputParameters: this.output_parameters.map(p => {
return p.accept(visitor);
}),
return_type: visitor.visitType?.(this.return_type)
});
return visitor.visitFunction?.(node) ?? node;
}
static fromXML(element, ns, options) {
let raw_name = element.$.name;
let name = sanitizeIdentifierName(null, raw_name);
if (hasShadow(element)) {
raw_name = element.$["shadows"];
name = sanitizeIdentifierName(null, element.$["shadows"]);
}
// TODO: Don't create a useless function object here
let fn = new IntrospectedFunction({
name,
raw_name,
namespace: ns,
isIntrospectable: isIntrospectable(element)
});
let return_type;
if ("return-value" in element && element["return-value"] && element["return-value"].length > 0) {
const value = element["return-value"][0];
return_type = getType(ns, value);
fn.returnTypeDoc = parseDoc(value);
}
else {
return_type = VoidType;
}
let parameters = [];
if (element.parameters) {
const param = element.parameters[0].parameter;
if (param) {
const inputs = param.filter((p) => {
return !!p.$.name;
});
parameters.push(...inputs.map(i => IntrospectedFunctionParameter.fromXML(i, fn, options)));
const unwrapped = return_type.unwrap();
const length_params = unwrapped instanceof ArrayType && unwrapped.length != null ? [unwrapped.length] : [];
const user_data_params = [];
parameters = parameters
.map(p => {
const unwrapped_type = p.type.unwrap();
if (unwrapped_type instanceof ArrayType && unwrapped_type.length != null) {
length_params.push(unwrapped_type.length);
return p;
}
if (unwrapped_type instanceof ClosureType && unwrapped_type.user_data != null) {
user_data_params.push(unwrapped_type.user_data);
return p;
}
return p;
})
.filter((_, i) => {
// We remove any length parameters.
return !length_params.includes(i) && !user_data_params.includes(i);
})
.filter(v => !(v.type instanceof TypeIdentifier && v.type.is("GLib", "DestroyNotify")))
.reverse()
.reduce(({ allowOptions, params }, p) => {
const { type, isOptional } = p;
if (allowOptions) {
if (type instanceof NullableType) {
params.push(p.copy({ isOptional: true }));
}
else if (!isOptional) {
params.push(p);
return { allowOptions: false, params };
}
else {
params.push(p);
}
}
else {
if (isOptional) {
params.push(p.copy({ type: new NullableType(type), isOptional: false }));
}
else {
params.push(p);
}
}
return { allowOptions, params };
}, {
allowOptions: true,
params: []
})
.params.reverse()
.filter((p) => p != null);
}
}
const input_parameters = parameters.filter(param => param.direction === GirDirection.In || param.direction === GirDirection.Inout);
const output_parameters = parameters
.filter(param => param.direction && (param.direction === GirDirection.Out || param.direction === GirDirection.Inout))
.map(parameter => parameter.copy({ isOptional: false }));
fn = fn.copy({
parent: ns,
parameters: input_parameters,
outputParameters: output_parameters,
return_type
});
if (options.loadDocs) {
fn.doc = parseDoc(element);
fn.metadata = parseMetadata(element);
}
return fn;
}
return() {
return this.return_type;
}
asCallback() {
const { name, raw_name, namespace, output_parameters, parameters, return_type } = this;
return new IntrospectedCallback({
namespace,
raw_name,
name,
output_parameters,
parameters,
return_type
});
}
asClassFunction(parent) {
const { raw_name: name, output_parameters, parameters, return_type, doc, isIntrospectable } = this;
const fn = new IntrospectedClassFunction({
parent,
name,
output_parameters,
parameters,
return_type,
doc,
isIntrospectable
});
fn.returnTypeDoc = this.returnTypeDoc;
fn.generics = [...this.generics];
return fn;
}
asVirtualClassFunction(parent) {
const { raw_name: name, output_parameters, parameters, return_type, doc, isIntrospectable } = this;
return new IntrospectedVirtualClassFunction({
parent,
name,
output_parameters,
parameters,
return_type,
doc,
isIntrospectable
});
}
asStaticClassFunction(parent) {
const { raw_name: name, output_parameters, parameters, return_type, doc, isIntrospectable } = this;
return new IntrospectedStaticClassFunction({
parent,
name,
output_parameters,
parameters,
return_type,
doc,
isIntrospectable
});
}
asString(generator) {
return generator.generateFunction(this);
}
}
export class IntrospectedDirectAllocationConstructor extends IntrospectedClassMember {
parameters;
constructor(parameters, parent) {
super("new", parent, { isPrivate: false, isIntrospectable: true });
this.parameters = parameters.map(parameter => parameter.copy({ parent: this }));
}
static fromFields(fields, parent) {
const building = new IntrospectedDirectAllocationConstructor([], parent);
building.parameters = fields
.filter(field => {
return !field.isStatic && !field.isPrivate && !field.type.isPointer;
})
.map(field => new IntrospectedFunctionParameter({
parent: building,
type: field.type,
name: field.name,
direction: GirDirection.In
}));
return building;
}
asString(generator) {
return generator.generateDirectAllocationConstructor(this);
}
copy(options) {
const copy = new IntrospectedDirectAllocationConstructor(options?.parameters ?? this.parameters, options?.parent ?? this.parent);
copy._copyBaseProperties(this);
return copy;
}
accept(visitor) {
const node = this.copy({
parameters: this.parameters.map(parameters => {
return parameters.accept(visitor);
})
});
return visitor.visitDirectAllocationConstructor?.(node) ?? node;
}
}
export class IntrospectedConstructor extends IntrospectedClassMember {
parameters = [];
return_type = UnknownType;
generics = [];
constructor({ name, parameters = [], return_type, parent, ...args }) {
super(name, parent, { ...args });
this.return_type = return_type;
this.parameters = parameters.map(p => p.copy({ parent: this }));
}
copy({ parent, parameters, return_type } = {}) {
const constr = new IntrospectedConstructor({
name: this.name,
parent: parent ?? this.parent ?? null,
return_type: return_type ?? this.return_type,
parameters: parameters ?? this.parameters
})._copyBaseProperties(this);
constr.generics = [...this.generics];
return constr;
}
static fromXML(m, parent, options) {
return IntrospectedClassFunction.fromXML(m, parent, options).asConstructor();
}
accept(visitor) {
const node = this.copy({
parameters: this.parameters.map(p => {
return p.accept(visitor);
}),
return_type: visitor.visitType?.(this.return_type)
});
return visitor.visitConstructor?.(node) ?? node;
}
return() {
return this.return_type;
}
asString(generator) {
return generator.generateConstructor(this);
}
}
export class IntrospectedFunctionParameter extends IntrospectedBase {
type;
direction;
isVarArgs = false;
isOptional = false;
isNullable = false;
constructor({ name, direction, type, parent, doc, isVarArgs = false, isOptional = false, isNullable = false, ...args }) {
super(name, parent ?? null, { ...args });
this.type = type;
this.direction = direction;
this.doc = doc;
this.isVarArgs = isVarArgs;
this.isOptional = isOptional;
this.isNullable = isNullable;
}
get namespace() {
if (!this.parent)
throw new Error(`Failed to get namespace for ${this.name}`);
return this.parent.namespace;
}
asField() {
const { name, parent, isOptional: optional, type } = this;
if (!(parent?.parent instanceof IntrospectedBaseClass)) {
throw new Error("Can't convert parameter of non-class function to field");
}
return new IntrospectedField({ name, parent: parent.parent, optional, type });
}
copy(options = {
parent: this.parent
}) {
const { type, parent, direction, isVarArgs, isOptional, isNullable, name, doc } = this;
return new IntrospectedFunctionParameter({
parent: options.parent ?? parent,
name,
direction,
isVarArgs,
isOptional: options.isOptional ?? isOptional,
isNullable: options.isNullable ?? isNullable,
type: options.type ?? type,
doc
})._copyBaseProperties(this);
}
accept(visitor) {
const node = this.copy({
type: visitor.visitType?.(this.type)
});
return visitor.visitParameter?.(node) ?? node;
}
asString(generator) {
return generator.generateParameter(this);
}
static fromXML(parameter, parent, options) {
const ns = parent.namespace;
let name = sanitizeMemberName(parameter.$.name);
if (isInvalid(name)) {
name = `_${name}`;
}
let isVarArgs = false;
let isOptional = false;
let isNullable = false;
let type;
let direction;
if (!parameter.$.direction ||
parameter.$.direction === GirDirection.In ||
parameter.$.direction === GirDirection.Inout ||
parameter.$.direction === GirDirection.InOut) {
if (name === "...") {
isVarArgs = true;
name = "args";
}
// Default to "in" direction
direction = parameter.$.direction || GirDirection.In;
const optional = parameter.$.optional === "1";
const nullable = parameter.$.nullable === "1";
if (optional) {
isOptional = true;
}
if (nullable) {
isNullable = true;
}
type = getType(ns, parameter);
}
else if (parameter.$.direction === GirDirection.Out) {
direction = parameter.$.direction;
type = getType(ns, parameter);
}
else {
throw new Error(`Unknown parameter direction: ${parameter.$.direction}`);
}
const fp = new IntrospectedFunctionParameter({
isVarArgs,
type: isVarArgs ? new ArrayType(type) : type,
direction,
parent: parent ?? undefined,
isOptional,
isNullable,
name,
isIntrospectable: isIntrospectable(parameter)
});
if (options.loadDocs) {
fp.doc = parseDoc(parameter);
fp.metadata = parseMetadata(parameter);
}
return fp;
}
}
export class IntrospectedClassFunction extends IntrospectedBase {
parameters;
return_type;
output_parameters;
_anyify = false;
_generify = false;
interfaceParent = null;
returnTypeDoc;
generics = [];
constructor({ name, parameters = [], output_parameters = [], return_type = UnknownType, parent, doc, ...args }) {
super(name, parent, { ...args });
this.parameters = parameters.map(p => p.copy({ parent: this }));
this.output_parameters = output_parameters.map(p => p.copy({ parent: this }));
this.return_type = return_type;
this.doc = doc;
}
get namespace() {
return this.parent.namespace;
}
asCallback() {
const { name, parent, output_parameters, parameters, return_type } = this;
return new IntrospectedClassCallback({
parent,
name,
output_parameters,
parameters,
return_type
});
}
asConstructor() {
const { name, parent, parameters } = this;
if (parent instanceof IntrospectedBaseClass) {
// Always force constructors to have the correct return type.
return new IntrospectedConstructor({
name,
parent,
parameters,
return_type: this.parent.getType(),
isIntrospectable: this.isIntrospectable
});
}
throw new Error(`Attempted to convert GirClassFunction into GirConstructor from invalid enum parent: ${this.parent.name}`);
}
asStaticClassFunction(parent) {
const { name, parameters, return_type, output_parameters } = this;
return new IntrospectedStaticClassFunction({
name,
parameters,
output_parameters,
return_type,
parent,
isIntrospectable: this.isIntrospectable
});
}
copy({ parent = this.parent, name, interfaceParent, parameters, outputParameters, returnType } = {}) {
const fn = new IntrospectedClassFunction({
name: name ?? this.name,
parent,
output_parameters: outputParameters ?? this.output_parameters,
parameters: parameters ?? this.parameters,
return_type: returnType ?? this.return_type
});
fn.generics = [...this.generics];
fn.returnTypeDoc = this.returnTypeDoc;
if (interfaceParent) {
fn.interfaceParent = interfaceParent;
}
return fn._copyBaseProperties(this);
}
accept(visitor) {
const node = this.copy({
parameters: this.parameters.map(p => {
return p.accept(visitor);
}),
outputParameters: this.output_parameters.map(p => {
return p.accept(visitor);
}),
returnType: visitor.visitType?.(this.return_type)
});
const fn = visitor.visitClassFunction?.(node);
return fn ?? node;
}
anyify() {
this._anyify = true;
return this;
}
shouldAnyify() {
return this._anyify;
}
static fromXML(element, parent, options) {
const fn = IntrospectedFunction.fromXML(element, parent.namespace, options);
return fn.asClassFunction(parent);
}
return() {
return this.return_type;
}
asString(generator) {
return generator.generateClassFunction(this);
}
}
export class IntrospectedVirtualClassFunction extends IntrospectedClassFunction {
constructor({ name, parameters = [], output_parameters = [], return_type = UnknownType, parent, doc, ...args }) {
super({
parent,
name: name.startsWith("vfunc_") ? name : `vfunc_${name}`,
parameters,
output_parameters,
return_type,
doc,
...args
});
}
copy({ parent = this.parent, interfaceParent, parameters, outputParameters, returnType }) {
const fn = new IntrospectedVirtualClassFunction({
name: this.name,
parent,
output_parameters: outputParameters ?? this.output_parameters,
parameters: parameters ?? this.parameters,
return_type: returnType ?? this.return_type
});
fn.generics = [...this.generics];
fn.returnTypeDoc = this.returnTypeDoc;
if (interfaceParent) {
fn.interfaceParent = interfaceParent;
}
return fn._copyBaseProperties(this);
}
return() {
return this.return_type;
}
accept(visitor) {
const node = this.copy({
parameters: this.parameters.map(p => {
return p.accept(visitor);
}),
outputParameters: this.output_parameters.map(p => {
return p.accept(visitor);
}),
returnType: visitor.visitType?.(this.return_type)
});
return visitor.visitVirtualClassFunction?.(node) ?? node;
}
static fromXML(m, parent, options) {
const fn = IntrospectedFunction.fromXML(m, parent.namespace, options);
return fn.asVirtualClassFunction(parent);
}
asString(generator) {
return generator.generateVirtualClassFunction(this);
}
}
export class IntrospectedStaticClassFunction extends IntrospectedClassFunction {
asString(generator) {
return generator.generateStaticClassFunction(this);
}
copy({ parent = this.parent, interfaceParent, parameters, outputParameters, returnType } = {}) {
const fn = new IntrospectedStaticClassFunction({
name: this.name,
parent,
output_parameters: outputParameters ?? this.output_parameters,
parameters: parameters ?? this.parameters,
return_type: returnType ?? this.return_type
});
fn.generics = [...this.generics];
fn.returnTypeDoc = this.returnTypeDoc;
if (interfaceParent) {
fn.interfaceParent = interfaceParent;
}
return fn._copyBaseProperties(this);
}
accept(visitor) {
const node = this.copy({
parent: this.parent,
parameters: this.parameters.map(p => {
return p.accept(visitor);
}),
outputParameters: this.output_parameters.map(p => {
return p.accept(visitor);
}),
returnType: visitor.visitType?.(this.return_type)
});
return visitor.visitStaticClassFunction?.(node) ?? node;
}
asClassFunction(parent) {
const { name, output_parameters, parameters, return_type, doc, isIntrospectable } = this;
const fn = new IntrospectedClassFunction({
parent,
name,
output_parameters,
parameters,
return_type,
doc,
isIntrospectable
});
fn.returnTypeDoc = this.returnTypeDoc;
fn.generics = [...this.generics];
return fn;
}
static fromXML(m, parent, options) {
const fn = IntrospectedFunction.fromXML(m, parent.namespace, options);
return fn.asStaticClassFunction(parent);
}
}
export class IntrospectedCallback extends IntrospectedFunction {
asFunctionType() {
return new FunctionType(Object.fromEntries(this.parameters.map(p => [p.name, p.type])), this.return_type);
}
copy({ parameters, returnType, outputParameters, parent } = {}) {
const cb = new IntrospectedCallback({
name: this.name,
raw_name: this.raw_name,
return_type: returnType ?? this.return_type,
parameters: parameters ?? this.parameters,
output_parameters: outputParameters ?? this.output_parameters,
namespace: parent ?? this.parent
})._copyBaseProperties(this);
cb.generics = [...this.generics];
return cb;
}
accept(visitor) {
const node = this.copy({
parameters: this.parameters.map(p => {
return p.accept(visitor);
}),
outputParameters: this.output_parameters.map(p => {
return p.accept(visitor);
}),
returnType: visitor.visitType?.(this.return_type)
});
return visitor.visitCallback?.(node) ?? node;
}
static fromXML(element, namespace, options) {
const ns = namespace;
const cb = IntrospectedFunction.fromXML(element, ns, options).asCallback();
const glibTypeName = element.$["glib:type-name"];
if (typeof glibTypeName === "string" && element.$["glib:type-name"]) {
cb.resolve_names.push(glibTypeName);
ns.registerResolveName(glibTypeName, ns.namespace, cb.name);
}
if (element.$["c:type"]) {
cb.resolve_names.push(element.$["c:type"]);
ns.registerResolveName(element.$["c:type"], ns.namespace, cb.name);
}
return cb;
}
asString(generator) {
return generator.generateCallback(this);
}
}
export class IntrospectedClassCallback extends IntrospectedClassFunction {
asFunctionType() {
return new FunctionType(Object.fromEntries(this.parameters.map(p => [p.name, p.type])), this.return_type);
}
copy({ parameters, returnType, outputParameters, parent } = {}) {
const cb = new IntrospectedClassCallback({
name: this.name,
return_type: returnType ?? this.return_type,
parameters: parameters ?? this.parameters,
output_parameters: outputParameters ?? this.output_parameters,
parent: parent ?? this.parent
})._copyBaseProperties(this);
cb.generics = [...this.generics];
return cb;
}
accept(visitor) {
const node = this.copy({
parameters: this.parameters.map(p => {
return p.accept(visitor);
}),
outputParameters: this.output_parameters.map(p => {
return p.accept(visitor);
}),
returnType: visitor.visitType?.(this.return_type)
});
return visitor.visitClassCallback?.(node) ?? node;
}
static fromXML(element, parent, options) {
const ns = parent.namespace;
const cb = IntrospectedClassFunction.fromXML(element, parent, options).asCallback();
const glibTypeName = element.$["glib:type-name"];
if (typeof glibTypeName === "string" && element.$["glib:type-name"]) {
cb.resolve_names.push(glibTypeName);
ns.registerResolveName(glibTypeName, ns.namespace, cb.name);
}
if (element.$["c:type"]) {
cb.resolve_names.push(element.$["c:type"]);
ns.registerResolveName(element.$["c:type"], ns.namespace, cb.name);
}
return cb;
}
asString(generator) {
return generator.generateClassCallback(this);
}
}
//# sourceMappingURL=function.js.map