Skip to content
Open
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
58 changes: 50 additions & 8 deletions Sources/SQLEngineWinMD/Database+SQL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@ public import WinMD
extension WinMD.Storage: SQLEngine.Catalog {
/// The optional tables the bundled queries reference — tables ECMA-335 lets a
/// database omit from the tables stream (`TypeSpec` §II.22.39 when nothing is
/// generic-instantiated, `NestedClass` §II.22.32 when nothing nests) that a
/// bundled view or the closure walk still names. `table(named:)` resolves an
/// generic-instantiated, `NestedClass` §II.22.32 when nothing nests,
/// `ClassLayout` §II.22.8 when no type declares an explicit layout or packing)
/// that a bundled view or the closure walk still names. `table(named:)`
/// resolves an
/// absent one to an empty relation and `relations()` enumerates it, so a
/// `SELECT … FROM` such a table reads no rows rather than faulting on a
/// missing relation. Only these are synthesised — not the whole table
Expand Down Expand Up @@ -1058,13 +1060,15 @@ extension WinMD.Storage {
/// undecodable signature, or an unresolvable one.
package borrowing func decode(return method: Int,
generics: Array<String>? = nil,
in dialect: Dialect) -> String? {
in dialect: Dialect,
qualifying: Set<String> = []) -> String? {
guard let table = opened("MethodDef") else { return nil }
let cursor = WinMD.Cursor(copy self, table)
guard let tuple = cursor[method - 1],
let row = Row<Metadata.Tables.MethodDef>(tuple),
let signature = try? row.prototype,
let resolver = try? Resolver(of: signature, with: self) else {
let resolver = try? Resolver(of: signature, with: self,
qualifying: qualifying) else {
return nil
}
return signature.returns.decode(generics: generics, with: resolver,
Expand All @@ -1087,7 +1091,8 @@ extension WinMD.Storage {
/// it, so threading it is always safe.
package borrowing func decode(parameter: Int,
generics: Array<String>? = nil,
for dialect: Dialect) -> String? {
for dialect: Dialect,
qualifying: Set<String> = []) -> String? {
guard let table = opened("Param") else { return nil }
let params = WinMD.Cursor(copy self, table)
guard let param = params[parameter - 1],
Expand All @@ -1107,7 +1112,8 @@ extension WinMD.Storage {
guard position >= 1, position <= signature.parameters.count else {
return nil
}
guard let resolver = try? Resolver(of: signature, with: self) else {
guard let resolver = try? Resolver(of: signature, with: self,
qualifying: qualifying) else {
return nil
}
let name = param.ordinal(for: "Name").flatMap { try? param.string($0) }
Expand All @@ -1126,13 +1132,15 @@ extension WinMD.Storage {
/// type through it. `nil` mirrors the same undecodable contract — an absent
/// row, an undecodable signature, or an unresolvable one — so the walk can
/// treat a malformed field as a frontier.
package borrowing func decode(field: Int, in dialect: Dialect) -> String? {
package borrowing func decode(field: Int, in dialect: Dialect,
qualifying: Set<String> = []) -> String? {
guard let table = opened("FieldDef") else { return nil }
let cursor = WinMD.Cursor(copy self, table)
guard let tuple = cursor[field - 1],
let row = Row<Metadata.Tables.FieldDef>(tuple),
let signature = try? row.declaration,
let resolver = try? Resolver(of: signature, with: self) else {
let resolver = try? Resolver(of: signature, with: self,
qualifying: qualifying) else {
return nil
}
return signature.type.decode(with: resolver, dialect: dialect)
Expand Down Expand Up @@ -1160,6 +1168,40 @@ extension WinMD.Storage {
return resolver.identities
}

/// The type identities the `MethodDef` at 1-based `method` `Id` names in its
/// *return* position — the return-spelled subset of `identities(method:)`, so
/// the closure walk categorizes a method's references as returned or
/// parameter. A reference named in both positions is in both subsets. An
/// absent or undecodable row yields the empty set.
package borrowing func returned(method: Int) -> Set<Referent> {
guard let table = opened("MethodDef") else { return [] }
let cursor = WinMD.Cursor(copy self, table)
guard let tuple = cursor[method - 1],
let row = Row<Metadata.Tables.MethodDef>(tuple),
let signature = try? row.prototype,
let resolver = try? Resolver(of: signature, with: self) else {
return []
}
return resolver.returned
}

/// The type identities the `MethodDef` at 1-based `method` `Id` names in its
/// *parameter* positions — the parameter-spelled subset of
/// `identities(method:)`; a reference named in both a return and a parameter
/// is in `returned(method:)` too. An absent or undecodable row yields the
/// empty set.
package borrowing func parameters(method: Int) -> Set<Referent> {
guard let table = opened("MethodDef") else { return [] }
let cursor = WinMD.Cursor(copy self, table)
guard let tuple = cursor[method - 1],
let row = Row<Metadata.Tables.MethodDef>(tuple),
let signature = try? row.prototype,
let resolver = try? Resolver(of: signature, with: self) else {
return []
}
return resolver.parameters
}

/// The distinct type identities named in the signature of the `FieldDef` at
/// 1-based `field` `Id` — the referenced-type set a struct's field contributes
/// to the closure walk (edge E6).
Expand Down
31 changes: 20 additions & 11 deletions Sources/SQLEngineWinMD/Resources/Queries/bases.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
CREATE VIEW bases AS
-- Each plain base carries the InterfaceImpl provenance the render resolves it
-- by: `ref` is the base's `Interface_TypeRef` Id (a reference the render follows
-- through the scope chain), `def` its `Interface_TypeDef` Id (a local definition
-- named directly). Exactly one is non-NULL per plain arm. The render keys the
-- inheritance spelling off the resolved *identity* rather than correlating the
-- name against a separate query — an external base that merely shares a bare
-- `TypeName` with a local nested interface must not adopt the local one's Id.
SELECT
b.TypeName AS base,
b.Id AS ref,
NULL AS def,
NULL AS spec
FROM
InterfaceImpl i
Expand All @@ -10,6 +19,8 @@ WHERE
UNION
SELECT
d.TypeName AS base,
NULL AS ref,
d.Id AS def,
NULL AS spec
FROM
InterfaceImpl i
Expand All @@ -18,19 +29,15 @@ WHERE
i.Class = :parent
UNION
-- A generic base interface is named through a TypeSpec: the InterfaceImpl's
-- Interface coded index tags TypeSpec, which has no TypeName of its own. A
-- generic instantiation (GENERICINST <base> <args…>) resolves to its generic
-- base, whose Id the adapter decodes to the TypeSpec's Base_TypeRef or
-- Base_TypeDef key -- the 1-based Id of the base in its table. So the name
-- reads through a seekable join against the TypeRef and TypeDef base tables,
-- mirroring the `identities` view's two TypeSpec arms without re-evaluating a
-- four-way UNION per InterfaceImpl row. Without these arms the generic base is
-- dropped. The TypeSpec's own Id rides alongside as `spec` (NULL on a plain
-- base): the generic definition's TypeName carries an invalid arity suffix and
-- drops the type arguments, so the render decodes the complete constructed
-- spelling from the TypeSpec signature rather than emitting that TypeName.
-- Interface coded index tags TypeSpec, which has no TypeName of its own. The
-- name reads through a seekable join against the TypeRef and TypeDef base
-- tables, mirroring the `identities` view's two TypeSpec arms. The TypeSpec's
-- own Id rides alongside as `spec` (NULL on a plain base); the render omits a
-- generic base (`spec IS NULL`), so its `ref`/`def` are NULL.
SELECT
r.TypeName AS base,
NULL AS ref,
NULL AS def,
i.Interface_TypeSpec AS spec
FROM
InterfaceImpl i
Expand All @@ -41,6 +48,8 @@ WHERE
UNION
SELECT
d.TypeName AS base,
NULL AS ref,
NULL AS def,
i.Interface_TypeSpec AS spec
FROM
InterfaceImpl i
Expand Down
Loading
Loading