Created
April 17, 2021 20:50
-
-
Save timsneath/fe1c2b778be7a2329ca3bb7a4bb2465e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file | |
// for details. All rights reserved. Use of this source code is governed by a | |
// BSD-style license that can be found in the LICENSE file. | |
import 'dart:ffi'; | |
import 'package:ffi/ffi.dart'; | |
import 'package:win32/win32.dart'; | |
import 'base.dart'; | |
import 'classlayout.dart'; | |
import 'com/IMetaDataImport2.dart'; | |
import 'constants.dart'; | |
import 'event.dart'; | |
import 'field.dart'; | |
import 'metadatastore.dart'; | |
import 'method.dart'; | |
import 'mixins/customattributes_mixin.dart'; | |
import 'mixins/genericparams_mixin.dart'; | |
import 'property.dart'; | |
import 'systemtokens.dart'; | |
import 'type_aliases.dart'; | |
import 'typeidentifier.dart'; | |
import 'utils.dart'; | |
enum TypeVisibility { | |
notPublic, | |
public, | |
nestedPublic, | |
nestedPrivate, | |
nestedFamily, | |
nestedAssembly, | |
nestedFamilyAndAssembly, | |
nestedFamilyOrAssembly | |
} | |
enum TypeLayout { auto, sequential, explicit } | |
enum StringFormat { ansi, unicode, auto, custom } | |
/// Represents a TypeDef in the Windows Metadata file | |
class TypeDef extends TokenObject | |
with CustomAttributesMixin, GenericParamsMixin { | |
final int baseTypeToken; | |
final String typeName; | |
final TypeIdentifier? typeSpec; | |
final int _attributes; | |
final _events = <Event>[]; | |
final _fields = <Field>[]; | |
final _interfaces = <TypeDef>[]; | |
final _methods = <Method>[]; | |
final _properties = <Property>[]; | |
/// Create a typedef. | |
/// | |
/// Typically, typedefs should be obtained from a [WinmdScope] object rather | |
/// than being created directly. | |
TypeDef(IMetaDataImport2 reader, | |
[int token = 0, | |
this.typeName = '', | |
this._attributes = 0, | |
this.baseTypeToken = 0, | |
this.typeSpec]) | |
: super(reader, token); | |
/// Creates a typedef object from its given token. | |
factory TypeDef.fromToken(IMetaDataImport2 reader, int token) { | |
switch (token & 0xFF000000) { | |
case CorTokenType.mdtTypeRef: | |
return TypeDef.fromTypeRefToken(reader, token); | |
case CorTokenType.mdtTypeDef: | |
return TypeDef.fromTypeDefToken(reader, token); | |
case CorTokenType.mdtTypeSpec: | |
return TypeDef.fromTypeSpecToken(reader, token); | |
default: | |
throw WinmdException('Unrecognized token ${token.toHexString(32)}'); | |
} | |
} | |
return TypeDef.fromToken(reader, interfaceToken); | |
token == 0 ? null : TypeDef.fromToken(reader, baseTypeToken); | |
/// Instantiate a typedef from a TypeDef token. | |
factory TypeDef.fromTypeDefToken(IMetaDataImport2 reader, int typeDefToken) { | |
final szTypeDef = stralloc(MAX_STRING_SIZE); | |
final pchTypeDef = calloc<ULONG>(); | |
final pdwTypeDefFlags = calloc<DWORD>(); | |
final ptkExtends = calloc<mdToken>(); | |
try { | |
final hr = reader.GetTypeDefProps(typeDefToken, szTypeDef, | |
MAX_STRING_SIZE, pchTypeDef, pdwTypeDefFlags, ptkExtends); | |
if (SUCCEEDED(hr)) { | |
return TypeDef(reader, typeDefToken, szTypeDef.toDartString(), | |
pdwTypeDefFlags.value, ptkExtends.value); | |
} else { | |
throw WindowsException(hr); | |
} | |
} finally { | |
free(pchTypeDef); | |
free(pdwTypeDefFlags); | |
free(ptkExtends); | |
free(szTypeDef); | |
} | |
} | |
/// Instantiate a typedef from a TypeRef token. | |
/// | |
/// Unless the TypeRef token is `IInspectable`, the COM parent interface for | |
/// Windows Runtime classes, the TypeRef is used to obtain the host scope | |
/// metadata file, from which the TypeDef can be found and returned. | |
factory TypeDef.fromTypeRefToken(IMetaDataImport2 reader, int typeRefToken) { | |
final ptkResolutionScope = calloc<mdToken>(); | |
final szName = stralloc(MAX_STRING_SIZE); | |
final pchName = calloc<ULONG>(); | |
try { | |
final hr = reader.GetTypeRefProps( | |
typeRefToken, ptkResolutionScope, szName, MAX_STRING_SIZE, pchName); | |
if (SUCCEEDED(hr)) { | |
final typeName = szName.toDartString(); | |
try { | |
final newScope = MetadataStore.getScopeForType(typeName); | |
return newScope.findTypeDef(typeName)!; | |
} catch (exception) { | |
// a token like IInspectable is out of reach of GetTypeRefProps, since it is | |
// a plain COM object. These objects are returned as system types. | |
if (systemTokens.containsKey(typeRefToken)) { | |
return TypeDef(reader, 0, systemTokens[typeRefToken]!); | |
} | |
if (systemTokens.containsValue(typeName)) { | |
return TypeDef(reader, 0, typeName); | |
} | |
// Perhaps we can find it in the current scope after all (for example, | |
// it's a nested class) | |
try { | |
final typedef = TypeDef.fromTypeDefToken(reader, typeRefToken); | |
return typedef; | |
} catch (exception) { | |
throw WinmdException( | |
'Unable to find scope for $typeName [${typeRefToken.toHexString(32)}]...'); | |
} | |
} | |
} else { | |
throw WindowsException(hr); | |
} | |
} finally { | |
free(ptkResolutionScope); | |
free(szName); | |
free(pchName); | |
} | |
} | |
/// Instantiate a typedef from a TypeSpec token. | |
factory TypeDef.fromTypeSpecToken( | |
IMetaDataImport2 reader, int typeSpecToken) { | |
final ppvSig = calloc<PCCOR_SIGNATURE>(); | |
final pcbSig = calloc<ULONG>(); | |
try { | |
final hr = | |
reader.GetTypeSpecFromToken(typeSpecToken, ppvSig.cast(), pcbSig); | |
final signature = ppvSig.value.asTypedList(pcbSig.value); | |
final typeTuple = parseTypeFromSignature(signature, reader); | |
if (SUCCEEDED(hr)) { | |
return TypeDef( | |
reader, typeSpecToken, '', 0, 0, typeTuple.typeIdentifier); | |
} else { | |
throw WindowsException(hr); | |
} | |
} finally { | |
free(ppvSig); | |
free(pcbSig); | |
} | |
} | |
@override | |
String toString() => 'TypeDef: $typeName'; | |
TypeVisibility get typeVisibility => | |
TypeVisibility.values[_attributes & CorTypeAttr.tdVisibilityMask]; | |
TypeLayout get typeLayout { | |
switch (_attributes & CorTypeAttr.tdLayoutMask) { | |
case CorTypeAttr.tdAutoLayout: | |
return TypeLayout.auto; | |
case CorTypeAttr.tdSequentialLayout: | |
return TypeLayout.sequential; | |
case CorTypeAttr.tdExplicitLayout: | |
return TypeLayout.explicit; | |
default: | |
throw WinmdException('Attribute missing type layout information'); | |
} | |
} | |
/// Is the type a class? | |
bool get isClass => | |
_attributes & CorTypeAttr.tdClassSemanticsMask == CorTypeAttr.tdClass; | |
/// Is the type an interface? | |
bool get isInterface => | |
_attributes & CorTypeAttr.tdClassSemanticsMask == CorTypeAttr.tdInterface; | |
bool get isAbstract => | |
_attributes & CorTypeAttr.tdAbstract == CorTypeAttr.tdAbstract; | |
bool get isSealed => | |
_attributes & CorTypeAttr.tdSealed == CorTypeAttr.tdSealed; | |
bool get isSpecialName => | |
_attributes & CorTypeAttr.tdSpecialName == CorTypeAttr.tdSpecialName; | |
bool get isImported => | |
_attributes & CorTypeAttr.tdImport == CorTypeAttr.tdImport; | |
bool get isSerializable => | |
_attributes & CorTypeAttr.tdSerializable == CorTypeAttr.tdSerializable; | |
bool get isWindowsRuntime => | |
_attributes & CorTypeAttr.tdWindowsRuntime == | |
CorTypeAttr.tdWindowsRuntime; | |
bool get isRTSpecialName => | |
_attributes & CorTypeAttr.tdRTSpecialName == CorTypeAttr.tdRTSpecialName; | |
StringFormat get stringFormat { | |
switch (_attributes & CorTypeAttr.tdStringFormatMask) { | |
case CorTypeAttr.tdAnsiClass: | |
return StringFormat.ansi; | |
case CorTypeAttr.tdUnicodeClass: | |
return StringFormat.unicode; | |
case CorTypeAttr.tdAutoClass: | |
return StringFormat.auto; | |
case CorTypeAttr.tdCustomFormatClass: | |
return StringFormat.custom; | |
default: | |
throw WinmdException('Attribute missing string format information'); | |
} | |
} | |
bool get isBeforeFieldInit => | |
_attributes & CorTypeAttr.tdBeforeFieldInit == | |
CorTypeAttr.tdBeforeFieldInit; | |
bool get isForwarder => | |
_attributes & CorTypeAttr.tdForwarder == CorTypeAttr.tdForwarder; | |
/// Is the type a delegate? | |
bool get isDelegate => parent?.typeName == 'System.MulticastDelegate'; | |
/// Retrieve class layout information. | |
/// | |
/// This includes the packing alignment, the minimum class size, and the field | |
/// layout (e.g. for sparsely or overlapping structs). | |
ClassLayout get classLayout => ClassLayout(reader, token); | |
/// Is the type a non-Windows Runtime type, such as System.Object or | |
/// IInspectable? | |
/// | |
/// More information at: | |
/// https://docs.microsoft.com/en-us/uwp/winrt-cref/winmd-files#type-system-encoding | |
bool get isSystemType => systemTokens.containsValue(typeName); | |
/// Converts an individual interface into a type. | |
TypeDef processInterfaceToken(int token) { | |
final ptkClass = calloc<mdTypeDef>(); | |
final ptkIface = calloc<mdToken>(); | |
try { | |
final hr = reader.GetInterfaceImplProps(token, ptkClass, ptkIface); | |
if (SUCCEEDED(hr)) { | |
final interfaceToken = ptkIface.value; | |
return TypeDef.fromToken(reader, interfaceToken); | |
} else { | |
throw WindowsException(hr); | |
} | |
} finally { | |
free(ptkClass); | |
free(ptkIface); | |
} | |
} | |
/// Enumerate all interfaces that this type implements. | |
List<TypeDef> get interfaces { | |
if (_interfaces.isEmpty) { | |
final phEnum = calloc<HCORENUM>(); | |
final rImpls = calloc<mdInterfaceImpl>(); | |
final pcImpls = calloc<ULONG>(); | |
try { | |
var hr = reader.EnumInterfaceImpls(phEnum, token, rImpls, 1, pcImpls); | |
while (hr == S_OK) { | |
final interfaceToken = rImpls.value; | |
_interfaces.add(processInterfaceToken(interfaceToken)); | |
hr = reader.EnumInterfaceImpls(phEnum, token, rImpls, 1, pcImpls); | |
} | |
} finally { | |
reader.CloseEnum(phEnum.value); | |
free(phEnum); | |
free(rImpls); | |
free(pcImpls); | |
} | |
} | |
return _interfaces; | |
} | |
/// Enumerate all fields contained within this type. | |
List<Field> get fields { | |
if (_fields.isEmpty) { | |
final phEnum = calloc<HCORENUM>(); | |
final rgFields = calloc<mdFieldDef>(); | |
final pcTokens = calloc<ULONG>(); | |
try { | |
var hr = reader.EnumFields(phEnum, token, rgFields, 1, pcTokens); | |
while (hr == S_OK) { | |
final fieldToken = rgFields.value; | |
_fields.add(Field.fromToken(reader, fieldToken)); | |
hr = reader.EnumFields(phEnum, token, rgFields, 1, pcTokens); | |
} | |
} finally { | |
reader.CloseEnum(phEnum.value); | |
free(phEnum); | |
free(rgFields); | |
free(pcTokens); | |
} | |
} | |
return _fields; | |
} | |
/// Enumerate all methods contained within this type. | |
List<Method> get methods { | |
if (_methods.isEmpty) { | |
final phEnum = calloc<HCORENUM>(); | |
final rgMethods = calloc<mdMethodDef>(); | |
final pcTokens = calloc<ULONG>(); | |
try { | |
var hr = reader.EnumMethods(phEnum, token, rgMethods, 1, pcTokens); | |
while (hr == S_OK) { | |
final methodToken = rgMethods.value; | |
_methods.add(Method.fromToken(reader, methodToken)); | |
hr = reader.EnumMethods(phEnum, token, rgMethods, 1, pcTokens); | |
} | |
} finally { | |
reader.CloseEnum(phEnum.value); | |
free(phEnum); | |
free(rgMethods); | |
free(pcTokens); | |
} | |
} | |
return _methods; | |
} | |
/// Enumerate all properties contained within this type. | |
List<Property> get properties { | |
if (_properties.isEmpty) { | |
final phEnum = calloc<HCORENUM>(); | |
final rgProperties = calloc<mdProperty>(); | |
final pcProperties = calloc<ULONG>(); | |
try { | |
var hr = | |
reader.EnumProperties(phEnum, token, rgProperties, 1, pcProperties); | |
while (hr == S_OK) { | |
final propertyToken = rgProperties.value; | |
_properties.add(Property.fromToken(reader, propertyToken)); | |
hr = reader.EnumMethods(phEnum, token, rgProperties, 1, pcProperties); | |
} | |
} finally { | |
reader.CloseEnum(phEnum.value); | |
free(phEnum); | |
free(rgProperties); | |
free(pcProperties); | |
} | |
} | |
return _properties; | |
} | |
/// Enumerate all events contained within this type. | |
List<Event> get events { | |
if (_events.isEmpty) { | |
final phEnum = calloc<HCORENUM>(); | |
final rgEvents = calloc<mdEvent>(); | |
final pcEvents = calloc<ULONG>(); | |
try { | |
var hr = reader.EnumEvents(phEnum, token, rgEvents, 1, pcEvents); | |
while (hr == S_OK) { | |
final eventToken = rgEvents.value; | |
_events.add(Event.fromToken(reader, eventToken)); | |
hr = reader.EnumEvents(phEnum, token, rgEvents, 1, pcEvents); | |
} | |
} finally { | |
reader.CloseEnum(phEnum.value); | |
free(phEnum); | |
free(rgEvents); | |
free(pcEvents); | |
} | |
} | |
return _events; | |
} | |
/// Get a field matching the name, if one exists. | |
/// | |
/// Returns null if the field is not found. | |
Field? findField(String fieldName) { | |
try { | |
return fields.firstWhere((field) => field.name == fieldName); | |
} on StateError { | |
return null; | |
} | |
} | |
/// Get a method matching the name, if one exists. | |
/// | |
/// Returns null if the method is not found. | |
Method? findMethod(String methodName) { | |
final szName = methodName.toNativeUtf16(); | |
final pmb = calloc<mdMethodDef>(); | |
try { | |
final hr = reader.FindMethod(token, szName, nullptr, 0, pmb); | |
if (SUCCEEDED(hr)) { | |
return Method.fromToken(reader, pmb.value); | |
} else if (hr == CLDB_E_RECORD_NOTFOUND) { | |
return null; | |
} else { | |
throw COMException(hr); | |
} | |
} finally { | |
free(szName); | |
free(pmb); | |
} | |
} | |
/// Gets the type referencing this type's superclass. | |
TypeDef? get parent => | |
token == 0 ? null : TypeDef.fromToken(reader, baseTypeToken); | |
String? getCustomGUIDAttribute(String guidAttributeName) { | |
final ptrAttributeName = guidAttributeName.toNativeUtf16(); | |
final ppData = calloc<Pointer<BYTE>>(); | |
final pcbData = calloc<ULONG>(); | |
try { | |
final hr = reader.GetCustomAttributeByName( | |
token, ptrAttributeName, ppData.cast(), pcbData); | |
if (SUCCEEDED(hr)) { | |
final blob = ppData.value; | |
if (pcbData.value > 0) { | |
final returnValue = blob.elementAt(2).cast<GUID>(); | |
return returnValue.ref.toString(); | |
} | |
} | |
} finally { | |
free(ptrAttributeName); | |
free(ppData); | |
free(pcbData); | |
} | |
} | |
/// Get the GUID for this type. | |
/// | |
/// Returns null if a GUID couldn't be found. | |
String? get guid { | |
var guid = | |
getCustomGUIDAttribute('Windows.Foundation.Metadata.GuidAttribute'); | |
guid ??= getCustomGUIDAttribute('Windows.Win32.Interop.GuidAttribute'); | |
return guid; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file | |
// for details. All rights reserved. Use of this source code is governed by a | |
// BSD-style license that can be found in the LICENSE file. | |
import 'dart:ffi'; | |
import 'package:ffi/ffi.dart'; | |
import 'package:win32/win32.dart'; | |
import 'base.dart'; | |
import 'classlayout.dart'; | |
import 'com/IMetaDataImport2.dart'; | |
import 'constants.dart'; | |
import 'event.dart'; | |
import 'field.dart'; | |
import 'metadatastore.dart'; | |
import 'method.dart'; | |
import 'mixins/customattributes_mixin.dart'; | |
import 'mixins/genericparams_mixin.dart'; | |
import 'property.dart'; | |
import 'systemtokens.dart'; | |
import 'type_aliases.dart'; | |
import 'typeidentifier.dart'; | |
import 'utils.dart'; | |
enum TypeVisibility { | |
notPublic, | |
public, | |
nestedPublic, | |
nestedPrivate, | |
nestedFamily, | |
nestedAssembly, | |
nestedFamilyAndAssembly, | |
nestedFamilyOrAssembly | |
} | |
enum TypeLayout { auto, sequential, explicit } | |
enum StringFormat { ansi, unicode, auto, custom } | |
/// Represents a TypeDef in the Windows Metadata file | |
class TypeDef extends TokenObject | |
with CustomAttributesMixin, GenericParamsMixin { | |
final String typeName; | |
final int _attributes; | |
final int baseTypeToken; | |
final TypeIdentifier? typeSpec; | |
final _interfaces = <TypeDef>[]; | |
final _fields = <Field>[]; | |
final _methods = <Method>[]; | |
final _properties = <Property>[]; | |
final _events = <Event>[]; | |
TypeVisibility get typeVisibility => | |
TypeVisibility.values[_attributes & CorTypeAttr.tdVisibilityMask]; | |
TypeLayout get typeLayout { | |
switch (_attributes & CorTypeAttr.tdLayoutMask) { | |
case CorTypeAttr.tdAutoLayout: | |
return TypeLayout.auto; | |
case CorTypeAttr.tdSequentialLayout: | |
return TypeLayout.sequential; | |
case CorTypeAttr.tdExplicitLayout: | |
return TypeLayout.explicit; | |
default: | |
throw WinmdException('Attribute missing type layout information'); | |
} | |
} | |
/// Is the type a class? | |
bool get isClass => | |
_attributes & CorTypeAttr.tdClassSemanticsMask == CorTypeAttr.tdClass; | |
/// Is the type an interface? | |
bool get isInterface => | |
_attributes & CorTypeAttr.tdClassSemanticsMask == CorTypeAttr.tdInterface; | |
bool get isAbstract => | |
_attributes & CorTypeAttr.tdAbstract == CorTypeAttr.tdAbstract; | |
bool get isSealed => | |
_attributes & CorTypeAttr.tdSealed == CorTypeAttr.tdSealed; | |
bool get isSpecialName => | |
_attributes & CorTypeAttr.tdSpecialName == CorTypeAttr.tdSpecialName; | |
bool get isImported => | |
_attributes & CorTypeAttr.tdImport == CorTypeAttr.tdImport; | |
bool get isSerializable => | |
_attributes & CorTypeAttr.tdSerializable == CorTypeAttr.tdSerializable; | |
bool get isWindowsRuntime => | |
_attributes & CorTypeAttr.tdWindowsRuntime == | |
CorTypeAttr.tdWindowsRuntime; | |
bool get isRTSpecialName => | |
_attributes & CorTypeAttr.tdRTSpecialName == CorTypeAttr.tdRTSpecialName; | |
StringFormat get stringFormat { | |
switch (_attributes & CorTypeAttr.tdStringFormatMask) { | |
case CorTypeAttr.tdAnsiClass: | |
return StringFormat.ansi; | |
case CorTypeAttr.tdUnicodeClass: | |
return StringFormat.unicode; | |
case CorTypeAttr.tdAutoClass: | |
return StringFormat.auto; | |
case CorTypeAttr.tdCustomFormatClass: | |
return StringFormat.custom; | |
default: | |
throw WinmdException('Attribute missing string format information'); | |
} | |
} | |
bool get isBeforeFieldInit => | |
_attributes & CorTypeAttr.tdBeforeFieldInit == | |
CorTypeAttr.tdBeforeFieldInit; | |
bool get isForwarder => | |
_attributes & CorTypeAttr.tdForwarder == CorTypeAttr.tdForwarder; | |
/// Is the type a delegate? | |
bool get isDelegate => parent?.typeName == 'System.MulticastDelegate'; | |
/// Retrieve class layout information. | |
/// | |
/// This includes the packing alignment, the minimum class size, and the field | |
/// layout (e.g. for sparsely or overlapping structs). | |
ClassLayout get classLayout => ClassLayout(reader, token); | |
/// Is the type a non-Windows Runtime type, such as System.Object or | |
/// IInspectable? | |
/// | |
/// More information at: | |
/// https://docs.microsoft.com/en-us/uwp/winrt-cref/winmd-files#type-system-encoding | |
bool get isSystemType => systemTokens.containsValue(typeName); | |
/// Create a typedef. | |
/// | |
/// Typically, typedefs should be obtained from a [WinmdScope] object rather | |
/// than being created directly. | |
TypeDef(IMetaDataImport2 reader, | |
[int token = 0, | |
this.typeName = '', | |
this._attributes = 0, | |
this.baseTypeToken = 0, | |
this.typeSpec]) | |
: super(reader, token); | |
/// Creates a typedef object from its given token. | |
factory TypeDef.fromToken(IMetaDataImport2 reader, int token) { | |
switch (token & 0xFF000000) { | |
case CorTokenType.mdtTypeRef: | |
return TypeDef.fromTypeRefToken(reader, token); | |
case CorTokenType.mdtTypeDef: | |
return TypeDef.fromTypeDefToken(reader, token); | |
case CorTokenType.mdtTypeSpec: | |
return TypeDef.fromTypeSpecToken(reader, token); | |
default: | |
throw WinmdException('Unrecognized token ${token.toHexString(32)}'); | |
} | |
} | |
/// Instantiate a typedef from a TypeDef token. | |
factory TypeDef.fromTypeDefToken(IMetaDataImport2 reader, int typeDefToken) { | |
final szTypeDef = stralloc(MAX_STRING_SIZE); | |
final pchTypeDef = calloc<ULONG>(); | |
final pdwTypeDefFlags = calloc<DWORD>(); | |
final ptkExtends = calloc<mdToken>(); | |
try { | |
final hr = reader.GetTypeDefProps(typeDefToken, szTypeDef, | |
MAX_STRING_SIZE, pchTypeDef, pdwTypeDefFlags, ptkExtends); | |
if (SUCCEEDED(hr)) { | |
return TypeDef(reader, typeDefToken, szTypeDef.toDartString(), | |
pdwTypeDefFlags.value, ptkExtends.value); | |
} else { | |
throw WindowsException(hr); | |
} | |
} finally { | |
free(pchTypeDef); | |
free(pdwTypeDefFlags); | |
free(ptkExtends); | |
free(szTypeDef); | |
} | |
} | |
/// Instantiate a typedef from a TypeRef token. | |
/// | |
/// Unless the TypeRef token is `IInspectable`, the COM parent interface for | |
/// Windows Runtime classes, the TypeRef is used to obtain the host scope | |
/// metadata file, from which the TypeDef can be found and returned. | |
factory TypeDef.fromTypeRefToken(IMetaDataImport2 reader, int typeRefToken) { | |
final ptkResolutionScope = calloc<mdToken>(); | |
final szName = stralloc(MAX_STRING_SIZE); | |
final pchName = calloc<ULONG>(); | |
try { | |
final hr = reader.GetTypeRefProps( | |
typeRefToken, ptkResolutionScope, szName, MAX_STRING_SIZE, pchName); | |
if (SUCCEEDED(hr)) { | |
final typeName = szName.toDartString(); | |
try { | |
final newScope = MetadataStore.getScopeForType(typeName); | |
return newScope.findTypeDef(typeName)!; | |
} catch (exception) { | |
// a token like IInspectable is out of reach of GetTypeRefProps, since it is | |
// a plain COM object. These objects are returned as system types. | |
if (systemTokens.containsKey(typeRefToken)) { | |
return TypeDef(reader, 0, systemTokens[typeRefToken]!); | |
} | |
if (systemTokens.containsValue(typeName)) { | |
return TypeDef(reader, 0, typeName); | |
} | |
// Perhaps we can find it in the current scope after all (for example, | |
// it's a nested class) | |
try { | |
final typedef = TypeDef.fromTypeDefToken(reader, typeRefToken); | |
return typedef; | |
} catch (exception) { | |
throw WinmdException( | |
'Unable to find scope for $typeName [${typeRefToken.toHexString(32)}]...'); | |
} | |
} | |
} else { | |
throw WindowsException(hr); | |
} | |
} finally { | |
free(ptkResolutionScope); | |
free(szName); | |
free(pchName); | |
} | |
} | |
/// Instantiate a typedef from a TypeSpec token. | |
factory TypeDef.fromTypeSpecToken( | |
IMetaDataImport2 reader, int typeSpecToken) { | |
final ppvSig = calloc<PCCOR_SIGNATURE>(); | |
final pcbSig = calloc<ULONG>(); | |
try { | |
final hr = | |
reader.GetTypeSpecFromToken(typeSpecToken, ppvSig.cast(), pcbSig); | |
final signature = ppvSig.value.asTypedList(pcbSig.value); | |
final typeTuple = parseTypeFromSignature(signature, reader); | |
if (SUCCEEDED(hr)) { | |
return TypeDef( | |
reader, typeSpecToken, '', 0, 0, typeTuple.typeIdentifier); | |
} else { | |
throw WindowsException(hr); | |
} | |
} finally { | |
free(ppvSig); | |
free(pcbSig); | |
} | |
} | |
/// Converts an individual interface into a type. | |
TypeDef processInterfaceToken(int token) { | |
final ptkClass = calloc<mdTypeDef>(); | |
final ptkIface = calloc<mdToken>(); | |
try { | |
final hr = reader.GetInterfaceImplProps(token, ptkClass, ptkIface); | |
if (SUCCEEDED(hr)) { | |
final interfaceToken = ptkIface.value; | |
return TypeDef.fromToken(reader, interfaceToken); | |
} else { | |
throw WindowsException(hr); | |
} | |
} finally { | |
free(ptkClass); | |
free(ptkIface); | |
} | |
} | |
/// Enumerate all interfaces that this type implements. | |
List<TypeDef> get interfaces { | |
if (_interfaces.isEmpty) { | |
final phEnum = calloc<HCORENUM>(); | |
final rImpls = calloc<mdInterfaceImpl>(); | |
final pcImpls = calloc<ULONG>(); | |
try { | |
var hr = reader.EnumInterfaceImpls(phEnum, token, rImpls, 1, pcImpls); | |
while (hr == S_OK) { | |
final interfaceToken = rImpls.value; | |
_interfaces.add(processInterfaceToken(interfaceToken)); | |
hr = reader.EnumInterfaceImpls(phEnum, token, rImpls, 1, pcImpls); | |
} | |
} finally { | |
reader.CloseEnum(phEnum.value); | |
free(phEnum); | |
free(rImpls); | |
free(pcImpls); | |
} | |
} | |
return _interfaces; | |
} | |
/// Enumerate all fields contained within this type. | |
List<Field> get fields { | |
if (_fields.isEmpty) { | |
final phEnum = calloc<HCORENUM>(); | |
final rgFields = calloc<mdFieldDef>(); | |
final pcTokens = calloc<ULONG>(); | |
try { | |
var hr = reader.EnumFields(phEnum, token, rgFields, 1, pcTokens); | |
while (hr == S_OK) { | |
final fieldToken = rgFields.value; | |
_fields.add(Field.fromToken(reader, fieldToken)); | |
hr = reader.EnumFields(phEnum, token, rgFields, 1, pcTokens); | |
} | |
} finally { | |
reader.CloseEnum(phEnum.value); | |
free(phEnum); | |
free(rgFields); | |
free(pcTokens); | |
} | |
} | |
return _fields; | |
} | |
/// Enumerate all methods contained within this type. | |
List<Method> get methods { | |
if (_methods.isEmpty) { | |
final phEnum = calloc<HCORENUM>(); | |
final rgMethods = calloc<mdMethodDef>(); | |
final pcTokens = calloc<ULONG>(); | |
try { | |
var hr = reader.EnumMethods(phEnum, token, rgMethods, 1, pcTokens); | |
while (hr == S_OK) { | |
final methodToken = rgMethods.value; | |
_methods.add(Method.fromToken(reader, methodToken)); | |
hr = reader.EnumMethods(phEnum, token, rgMethods, 1, pcTokens); | |
} | |
} finally { | |
reader.CloseEnum(phEnum.value); | |
free(phEnum); | |
free(rgMethods); | |
free(pcTokens); | |
} | |
} | |
return _methods; | |
} | |
/// Enumerate all properties contained within this type. | |
List<Property> get properties { | |
if (_properties.isEmpty) { | |
final phEnum = calloc<HCORENUM>(); | |
final rgProperties = calloc<mdProperty>(); | |
final pcProperties = calloc<ULONG>(); | |
try { | |
var hr = | |
reader.EnumProperties(phEnum, token, rgProperties, 1, pcProperties); | |
while (hr == S_OK) { | |
final propertyToken = rgProperties.value; | |
_properties.add(Property.fromToken(reader, propertyToken)); | |
hr = reader.EnumMethods(phEnum, token, rgProperties, 1, pcProperties); | |
} | |
} finally { | |
reader.CloseEnum(phEnum.value); | |
free(phEnum); | |
free(rgProperties); | |
free(pcProperties); | |
} | |
} | |
return _properties; | |
} | |
/// Enumerate all events contained within this type. | |
List<Event> get events { | |
if (_events.isEmpty) { | |
final phEnum = calloc<HCORENUM>(); | |
final rgEvents = calloc<mdEvent>(); | |
final pcEvents = calloc<ULONG>(); | |
try { | |
var hr = reader.EnumEvents(phEnum, token, rgEvents, 1, pcEvents); | |
while (hr == S_OK) { | |
final eventToken = rgEvents.value; | |
_events.add(Event.fromToken(reader, eventToken)); | |
hr = reader.EnumEvents(phEnum, token, rgEvents, 1, pcEvents); | |
} | |
} finally { | |
reader.CloseEnum(phEnum.value); | |
free(phEnum); | |
free(rgEvents); | |
free(pcEvents); | |
} | |
} | |
return _events; | |
} | |
/// Get a field matching the name, if one exists. | |
/// | |
/// Returns null if the field is not found. | |
Field? findField(String fieldName) { | |
try { | |
return fields.firstWhere((field) => field.name == fieldName); | |
} on StateError { | |
return null; | |
} | |
} | |
/// Get a method matching the name, if one exists. | |
/// | |
/// Returns null if the method is not found. | |
Method? findMethod(String methodName) { | |
final szName = methodName.toNativeUtf16(); | |
final pmb = calloc<mdMethodDef>(); | |
try { | |
final hr = reader.FindMethod(token, szName, nullptr, 0, pmb); | |
if (SUCCEEDED(hr)) { | |
return Method.fromToken(reader, pmb.value); | |
} else if (hr == CLDB_E_RECORD_NOTFOUND) { | |
return null; | |
} else { | |
throw COMException(hr); | |
} | |
} finally { | |
free(szName); | |
free(pmb); | |
} | |
} | |
/// Gets the type referencing this type's superclass. | |
TypeDef? get parent => | |
token == 0 ? null : TypeDef.fromToken(reader, baseTypeToken); | |
String? getCustomGUIDAttribute(String guidAttributeName) { | |
final ptrAttributeName = guidAttributeName.toNativeUtf16(); | |
final ppData = calloc<Pointer<BYTE>>(); | |
final pcbData = calloc<ULONG>(); | |
try { | |
final hr = reader.GetCustomAttributeByName( | |
token, ptrAttributeName, ppData.cast(), pcbData); | |
if (SUCCEEDED(hr)) { | |
final blob = ppData.value; | |
if (pcbData.value > 0) { | |
final returnValue = blob.elementAt(2).cast<GUID>(); | |
return returnValue.ref.toString(); | |
} | |
} | |
} finally { | |
free(ptrAttributeName); | |
free(ppData); | |
free(pcbData); | |
} | |
} | |
/// Get the GUID for this type. | |
/// | |
/// Returns null if a GUID couldn't be found. | |
String? get guid { | |
var guid = | |
getCustomGUIDAttribute('Windows.Foundation.Metadata.GuidAttribute'); | |
guid ??= getCustomGUIDAttribute('Windows.Win32.Interop.GuidAttribute'); | |
return guid; | |
} | |
@override | |
String toString() => 'TypeDef: $typeName'; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment