Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mark enum parameters as nullable #213

Merged
merged 1 commit into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 39 additions & 39 deletions packages/lib/src/gir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export { promisifyFunctions } from './gir/promisify.js'
export { resolveDirectedType, resolvePrimitiveType } from './gir/util.js'
export * from './gir/nodes.js'

import type { OptionsGeneration } from './types/index.js'
import type { OptionsBase } from './types/index.js'

export abstract class TypeExpression {
isPointer = false
Expand All @@ -29,10 +29,10 @@ export abstract class TypeExpression {
}

abstract rewrap(type: TypeExpression): TypeExpression
abstract resolve(namespace: IntrospectedNamespace, options: OptionsGeneration): TypeExpression
abstract resolve(namespace: IntrospectedNamespace, options: OptionsBase): TypeExpression

abstract print(namespace: IntrospectedNamespace, options: OptionsGeneration): string
rootPrint(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
abstract print(namespace: IntrospectedNamespace, options: OptionsBase): string
rootPrint(namespace: IntrospectedNamespace, options: OptionsBase): string {
return this.print(namespace, options)
}
}
Expand Down Expand Up @@ -72,7 +72,7 @@ export class TypeIdentifier extends TypeExpression {
return new TypeIdentifier(sanitizeIdentifierName(this.namespace, this.name), sanitizeNamespace(this.namespace))
}

protected _resolve(namespace: IntrospectedNamespace, options: OptionsGeneration): TypeIdentifier | null {
protected _resolve(namespace: IntrospectedNamespace, options: OptionsBase): TypeIdentifier | null {
const name: string = sanitizeIdentifierName(null, this.name)
const unresolvedNamespaceName = this.namespace

Expand Down Expand Up @@ -144,11 +144,11 @@ export class TypeIdentifier extends TypeExpression {
return null
}

resolveIdentifier(namespace: IntrospectedNamespace, options: OptionsGeneration): TypeIdentifier | null {
resolveIdentifier(namespace: IntrospectedNamespace, options: OptionsBase): TypeIdentifier | null {
return this._resolve(namespace, options)
}

resolve(namespace: IntrospectedNamespace, options: OptionsGeneration): TypeExpression {
resolve(namespace: IntrospectedNamespace, options: OptionsBase): TypeExpression {
const resolved = this._resolve(namespace, options)

// Generally if we can't resolve a type it is not introspectable,
Expand All @@ -161,7 +161,7 @@ export class TypeIdentifier extends TypeExpression {
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
print(namespace: IntrospectedNamespace, _options: OptionsGeneration): string {
print(namespace: IntrospectedNamespace, _options: OptionsBase): string {
if (namespace.hasSymbol(this.namespace) && this.namespace !== namespace.namespace) {
// TODO: Move to TypeScript generator...
// Libraries like zbar have classes named things like "Gtk"
Expand Down Expand Up @@ -212,12 +212,12 @@ export class ModuleTypeIdentifier extends TypeIdentifier {
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected _resolve(namespace: IntrospectedNamespace, options: OptionsGeneration): ModuleTypeIdentifier | null {
protected _resolve(namespace: IntrospectedNamespace, options: OptionsBase): ModuleTypeIdentifier | null {
return this
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
print(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
print(namespace: IntrospectedNamespace, options: OptionsBase): string {
if (namespace.namespace === this.namespace) {
return `${this.moduleName}.${this.name}`
} else {
Expand All @@ -239,7 +239,7 @@ export class ClassStructTypeIdentifier extends TypeIdentifier {
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
print(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
print(namespace: IntrospectedNamespace, options: OptionsBase): string {
if (namespace.namespace === this.namespace) {
// TODO: Mapping to invalid names should happen at the generator level...
return `typeof ${isInvalid(this.name) ? `__${this.name}` : this.name}`
Expand All @@ -257,7 +257,7 @@ export class GenerifiedTypeIdentifier extends TypeIdentifier {
this.generics = generics
}

print(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
print(namespace: IntrospectedNamespace, options: OptionsBase): string {
const Generics = this.generics.map((generic) => generic.print(namespace, options)).join(', ')

if (namespace.namespace === this.namespace) {
Expand All @@ -267,7 +267,7 @@ export class GenerifiedTypeIdentifier extends TypeIdentifier {
}
}

_resolve(namespace: IntrospectedNamespace, options: OptionsGeneration): TypeIdentifier | null {
_resolve(namespace: IntrospectedNamespace, options: OptionsBase): TypeIdentifier | null {
const iden = super._resolve(namespace, options)

if (iden) {
Expand All @@ -279,9 +279,9 @@ export class GenerifiedTypeIdentifier extends TypeIdentifier {
}

export class NativeType extends TypeExpression {
readonly expression: (options?: OptionsGeneration) => string
readonly expression: (options?: OptionsBase) => string

constructor(expression: ((options?: OptionsGeneration) => string) | string) {
constructor(expression: ((options?: OptionsBase) => string) | string) {
super()

this.expression = typeof expression === 'string' ? () => expression : expression
Expand All @@ -295,19 +295,19 @@ export class NativeType extends TypeExpression {
return this
}

print(_namespace: IntrospectedNamespace, options: OptionsGeneration) {
print(_namespace: IntrospectedNamespace, options: OptionsBase) {
return this.expression(options)
}

equals(type: TypeExpression, options?: OptionsGeneration): boolean {
equals(type: TypeExpression, options?: OptionsBase): boolean {
return type instanceof NativeType && this.expression(options) === type.expression(options)
}

unwrap(): TypeExpression {
return this
}

static withGenerator(generator: (options?: OptionsGeneration) => string): TypeExpression {
static withGenerator(generator: (options?: OptionsBase) => string): TypeExpression {
return new NativeType(generator)
}

Expand All @@ -332,17 +332,17 @@ export class OrType extends TypeExpression {
return this
}

resolve(namespace: IntrospectedNamespace, options: OptionsGeneration): TypeExpression {
resolve(namespace: IntrospectedNamespace, options: OptionsBase): TypeExpression {
const [type, ...types] = this.types

return new OrType(type.resolve(namespace, options), ...types.map((t) => t.resolve(namespace, options)))
}

print(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
print(namespace: IntrospectedNamespace, options: OptionsBase): string {
return `(${this.types.map((t) => t.print(namespace, options)).join(' | ')})`
}

rootPrint(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
rootPrint(namespace: IntrospectedNamespace, options: OptionsBase): string {
return `${this.types.map((t) => t.print(namespace, options)).join(' | ')}`
}

Expand All @@ -356,15 +356,15 @@ export class OrType extends TypeExpression {
}

export class TupleType extends OrType {
print(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
print(namespace: IntrospectedNamespace, options: OptionsBase): string {
return `[${this.types.map((t) => t.print(namespace, options)).join(', ')}]`
}

rootPrint(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
rootPrint(namespace: IntrospectedNamespace, options: OptionsBase): string {
return this.print(namespace, options)
}

resolve(namespace: IntrospectedNamespace, options: OptionsGeneration): TypeExpression {
resolve(namespace: IntrospectedNamespace, options: OptionsBase): TypeExpression {
const [type, ...types] = this.types

return new TupleType(type.resolve(namespace, options), ...types.map((t) => t.resolve(namespace, options)))
Expand All @@ -388,7 +388,7 @@ export class BinaryType extends OrType {
return this
}

resolve(namespace: IntrospectedNamespace, options: OptionsGeneration) {
resolve(namespace: IntrospectedNamespace, options: OptionsBase) {
return new BinaryType(this.a.resolve(namespace, options), this.b.resolve(namespace, options))
}

Expand Down Expand Up @@ -440,7 +440,7 @@ export class FunctionType extends TypeExpression {
return this
}

resolve(namespace: IntrospectedNamespace, options: OptionsGeneration): TypeExpression {
resolve(namespace: IntrospectedNamespace, options: OptionsBase): TypeExpression {
return new FunctionType(
Object.fromEntries(
Object.entries(this.parameterTypes).map(([k, p]) => {
Expand All @@ -451,7 +451,7 @@ export class FunctionType extends TypeExpression {
)
}

rootPrint(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
rootPrint(namespace: IntrospectedNamespace, options: OptionsBase): string {
const Parameters = Object.entries(this.parameterTypes)
.map(([k, v]) => {
return `${k}: ${v.rootPrint(namespace, options)}`
Expand All @@ -461,7 +461,7 @@ export class FunctionType extends TypeExpression {
return `(${Parameters}) => ${this.returnType.print(namespace, options)}`
}

print(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
print(namespace: IntrospectedNamespace, options: OptionsBase): string {
return `(${this.rootPrint(namespace, options)})`
}
}
Expand Down Expand Up @@ -523,19 +523,19 @@ export class GenerifiedType extends TypeExpression {
this.generic = generic
}

resolve(namespace: IntrospectedNamespace, options: OptionsGeneration) {
resolve(namespace: IntrospectedNamespace, options: OptionsBase) {
return new GenerifiedType(this.type.resolve(namespace, options), this.generic.resolve())
}

unwrap() {
return this.type
}

rootPrint(namespace: IntrospectedNamespace, options: OptionsGeneration) {
rootPrint(namespace: IntrospectedNamespace, options: OptionsBase) {
return `${this.type.print(namespace, options)}<${this.generic.print()}>`
}

print(namespace: IntrospectedNamespace, options: OptionsGeneration) {
print(namespace: IntrospectedNamespace, options: OptionsBase) {
return `${this.type.print(namespace, options)}<${this.generic.print()}>`
}

Expand Down Expand Up @@ -625,11 +625,11 @@ export class PromiseType extends TypeExpression {
return new PromiseType(this.type.rewrap(type))
}

resolve(namespace: IntrospectedNamespace, options: OptionsGeneration): TypeExpression {
resolve(namespace: IntrospectedNamespace, options: OptionsBase): TypeExpression {
return new PromiseType(this.type.resolve(namespace, options))
}

print(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
print(namespace: IntrospectedNamespace, options: OptionsBase): string {
// TODO: Optimize this check.
if (!namespace.hasSymbol('Promise')) {
return `Promise<${this.type.print(namespace, options)}>`
Expand Down Expand Up @@ -693,15 +693,15 @@ export class TypeConflict extends TypeExpression {
return true
}

resolve(namespace: IntrospectedNamespace, options: OptionsGeneration): TypeExpression {
resolve(namespace: IntrospectedNamespace, options: OptionsBase): TypeExpression {
throw new Error(
`Type conflict was not resolved for ${this.type.resolve(namespace, options).print(namespace, options)} in ${
namespace.namespace
}`,
)
}

print(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
print(namespace: IntrospectedNamespace, options: OptionsBase): string {
throw new Error(
`Type conflict was not resolved for ${this.type.resolve(namespace, options).print(namespace, options)} in ${
namespace.namespace
Expand Down Expand Up @@ -744,7 +744,7 @@ export class ClosureType extends TypeExpression {
return this
}

resolve(namespace: IntrospectedNamespace, options: OptionsGeneration) {
resolve(namespace: IntrospectedNamespace, options: OptionsBase) {
const { user_data, type } = this

return ClosureType.new({
Expand All @@ -753,7 +753,7 @@ export class ClosureType extends TypeExpression {
})
}

print(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
print(namespace: IntrospectedNamespace, options: OptionsBase): string {
return this.type.print(namespace, options)
}

Expand Down Expand Up @@ -801,7 +801,7 @@ export class ArrayType extends TypeExpression {
return false
}

resolve(namespace: IntrospectedNamespace, options: OptionsGeneration): TypeExpression {
resolve(namespace: IntrospectedNamespace, options: OptionsBase): TypeExpression {
const { type, arrayDepth, length } = this
return ArrayType.new({
type: type.resolve(namespace, options),
Expand All @@ -810,7 +810,7 @@ export class ArrayType extends TypeExpression {
})
}

print(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
print(namespace: IntrospectedNamespace, options: OptionsBase): string {
const depth = this.arrayDepth
let typeSuffix: string = ''

Expand Down
6 changes: 4 additions & 2 deletions packages/lib/src/gir/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ParsedGir } from "../types/parsed-gir.js";
import { GirModule } from "../index.js";

import type { OptionsGeneration, OptionsTransform } from "../types/index.js";
import { FunctionParametersVisitor } from "../validators/function-parameters.js";

export interface NSLoader {
load(namespace: string, version: string): ParsedGir | null;
Expand Down Expand Up @@ -147,13 +148,14 @@ export class NSRegistry {
GObject.package_version = [...GLib.package_version];

const interfaceVisitor = new InterfaceVisitor();

this.registerTransformation(interfaceVisitor);

const classVisitor = new ClassVisitor();

this.registerTransformation(classVisitor);

const enumParamsVisitor = new FunctionParametersVisitor();
this.registerTransformation(enumParamsVisitor);

console.log("Adding generics...");
generify(this, options.inferGenerics);

Expand Down
52 changes: 52 additions & 0 deletions packages/lib/src/validators/function-parameters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { IntrospectedFunction, IntrospectedClassFunction } from "../gir/function.js";
import { IntrospectedBaseClass } from "../gir/class.js";
import { IntrospectedEnum } from "../gir/enum.js";

import { TypeIdentifier, NullableType } from "../gir.js";
import { GirVisitor } from "../visitor.js";

export class FunctionParametersVisitor extends GirVisitor {
/**
* Marks all enum parameters of a function as nullable,
* because GJS allows null values for enum parameters and treats them as a 0 value.
* See issue [#207](https://github.com/gjsify/ts-for-gir/issues/207).
*/
private makeEnumParamsNullable(node: IntrospectedFunction): IntrospectedFunction;
private makeEnumParamsNullable<T extends IntrospectedBaseClass | IntrospectedEnum>(
node: IntrospectedClassFunction<T>
): IntrospectedClassFunction<T>;
private makeEnumParamsNullable(
node: IntrospectedFunction | IntrospectedClassFunction
): IntrospectedFunction | IntrospectedClassFunction {
return node.copy({
parameters: node.parameters.map(param => {
const type = param.type.deepUnwrap();
if (type instanceof TypeIdentifier) {
// Get the namespace where this type should be defined
const ns = node.namespace.assertInstalledImport(type.namespace);

// Check if the type is an enum
const isEnumType = !!ns.getEnum(type.name);

// If it is, make the parameter nullable
if (isEnumType) {
return param.copy({
type: new NullableType(param.type)
});
}
}
return param;
})
});
}

visitFunction = (node: IntrospectedFunction): IntrospectedFunction => {
return this.makeEnumParamsNullable(node);
};

visitClassFunction = <T extends IntrospectedBaseClass | IntrospectedEnum>(
node: IntrospectedClassFunction<T>
): IntrospectedClassFunction<T> => {
return this.makeEnumParamsNullable(node);
};
}
Loading