- Updated import paths in multiple files to point to the new repository at git.savin.nyc/alex/go-winrt. - Removed old import paths referencing saltosystems/winrt-go. - Ensured consistency across all affected files in the Bluetooth Generic Attribute Profile and Foundation packages.
1261 lines
37 KiB
Go
1261 lines
37 KiB
Go
package codegen
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"go/format"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"git.savin.nyc/alex/go-winrt"
|
|
"git.savin.nyc/alex/go-winrt/internal/winmd"
|
|
"github.com/go-kit/log"
|
|
"github.com/go-kit/log/level"
|
|
"github.com/tdakkota/win32metadata/types"
|
|
"golang.org/x/tools/imports"
|
|
)
|
|
|
|
// Delegate constants
|
|
const (
|
|
invokeMethodName = "Invoke"
|
|
)
|
|
|
|
type generator struct {
|
|
class string
|
|
validateOnly bool
|
|
methodFilter *MethodFilter
|
|
|
|
logger log.Logger
|
|
|
|
genDataFiles []*genDataFile
|
|
|
|
mdStore *winmd.Store
|
|
}
|
|
|
|
// Generate generates the code for the given config.
|
|
func Generate(cfg *Config, logger log.Logger) error {
|
|
if err := cfg.Validate(); err != nil {
|
|
return err
|
|
}
|
|
|
|
mdStore, err := winmd.NewStore(logger)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
g := &generator{
|
|
class: cfg.Class,
|
|
validateOnly: cfg.ValidateOnly,
|
|
methodFilter: cfg.MethodFilter(),
|
|
logger: logger,
|
|
mdStore: mdStore,
|
|
}
|
|
return g.run()
|
|
}
|
|
|
|
func (g *generator) run() error {
|
|
_ = level.Debug(g.logger).Log("msg", "starting code generation", "class", g.class)
|
|
|
|
typeDef, err := g.mdStore.TypeDefByName(g.class)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return g.generate(typeDef)
|
|
}
|
|
|
|
func (g *generator) generate(typeDef *winmd.TypeDef) error {
|
|
|
|
// we only support WinRT types: check the tdWindowsRuntime flag (0x4000)
|
|
// https://docs.microsoft.com/en-us/uwp/winrt-cref/winmd-files#runtime-classes
|
|
if typeDef.Flags&0x4000 == 0 {
|
|
return fmt.Errorf("%s.%s is not a WinRT class", typeDef.TypeNamespace, typeDef.TypeName)
|
|
}
|
|
|
|
// get data & execute templates
|
|
if err := g.loadCodeGenData(typeDef); err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, fData := range g.genDataFiles {
|
|
if err := g.generateDataFile(fData, typeDef); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (g *generator) generateDataFile(fData *genDataFile, typeDef *winmd.TypeDef) error {
|
|
// get templates
|
|
tmpl, err := getTemplates()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fData.Data.ComputeImports(typeDef)
|
|
|
|
var buf bytes.Buffer
|
|
if err := tmpl.ExecuteTemplate(&buf, "file.tmpl", fData.Data); err != nil {
|
|
return err
|
|
}
|
|
|
|
// use go imports to cleanup imports
|
|
goimported, err := imports.Process(fData.Filename, buf.Bytes(), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// format the output source code
|
|
formatted, err := format.Source(goimported)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if g.validateOnly {
|
|
// validate existing file content
|
|
return g.validateFileContent(fData, formatted)
|
|
}
|
|
|
|
// create file & write contents
|
|
return g.writeFile(fData, formatted)
|
|
}
|
|
|
|
func (g *generator) validateFileContent(fData *genDataFile, genContent []byte) error {
|
|
// validate existing content
|
|
existingContent, err := os.ReadFile(fData.Filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// compare existing content to generated
|
|
_ = level.Debug(g.logger).Log("msg", "validating generated code", "filename", fData.Filename)
|
|
if string(existingContent) != string(genContent) {
|
|
return fmt.Errorf("file %s does not contain expected content", fData.Filename)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (g *generator) writeFile(fData *genDataFile, content []byte) error {
|
|
parts := strings.Split(fData.Filename, "/")
|
|
folder := strings.Join(parts[:len(parts)-1], "/")
|
|
err := os.MkdirAll(folder, os.ModePerm)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
file, err := os.Create(filepath.Clean(fData.Filename))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { _ = file.Close() }()
|
|
|
|
// and write it to file
|
|
_, err = file.Write(content)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (g *generator) loadCodeGenData(typeDef *winmd.TypeDef) error {
|
|
f := g.addFile(typeDef, "")
|
|
|
|
switch {
|
|
case typeDef.IsInterface():
|
|
_ = level.Info(g.logger).Log("msg", "generating interface", "interface", typeDef.TypeNamespace+"."+typeDef.TypeName)
|
|
|
|
if err := g.validateInterface(typeDef); err != nil {
|
|
return err
|
|
}
|
|
|
|
iface, err := g.createGenInterface(typeDef, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f.Data.Interfaces = append(f.Data.Interfaces, iface)
|
|
case typeDef.IsEnum():
|
|
_ = level.Info(g.logger).Log("msg", "generating enum", "enum", typeDef.TypeNamespace+"."+typeDef.TypeName)
|
|
|
|
enum, err := g.createGenEnum(typeDef)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f.Data.Enums = append(f.Data.Enums, enum)
|
|
case typeDef.IsStruct():
|
|
_ = level.Info(g.logger).Log("msg", "generating struct", "struct", typeDef.TypeNamespace+"."+typeDef.TypeName)
|
|
|
|
genStruct, err := g.createGenStruct(typeDef)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f.Data.Structs = append(f.Data.Structs, genStruct)
|
|
case typeDef.IsDelegate():
|
|
delegate, err := g.createGenDelegate(typeDef)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f.Data.Delegates = append(f.Data.Delegates, delegate)
|
|
default:
|
|
_ = level.Info(g.logger).Log("msg", "generating class", "class", typeDef.TypeNamespace+"."+typeDef.TypeName)
|
|
|
|
class, err := g.createGenClass(typeDef)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f.Data.Classes = append(f.Data.Classes, class)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (g *generator) addFile(typeDef *winmd.TypeDef, suffix string) *genDataFile {
|
|
folder := typeToFolder(typeDef.TypeNamespace, typeDef.TypeName)
|
|
filename := folder + "/" + typeFilename(typeDef.TypeName) + suffix + ".go"
|
|
f := genDataFile{
|
|
Filename: filename,
|
|
Data: genData{
|
|
Package: typePackage(typeDef.TypeNamespace, typeDef.TypeName),
|
|
},
|
|
}
|
|
g.genDataFiles = append(g.genDataFiles, &f)
|
|
return &f
|
|
}
|
|
|
|
func (g *generator) validateInterface(typeDef *winmd.TypeDef) error {
|
|
// Any WinRT interface with private visibility must have a single ExclusiveToAttribute.
|
|
// the ExclusiveToAttribute must reference a runtime class.
|
|
|
|
// we do not support generating these types of classes, they are exclusive to a runtime class,
|
|
// and thus will be generated when the runtime class is generated.
|
|
|
|
if typeDef.Flags.NotPublic() {
|
|
return fmt.Errorf("interface %s is not public", typeDef.TypeNamespace+"."+typeDef.TypeName)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// https://docs.microsoft.com/en-us/uwp/winrt-cref/winmd-files#interfaces
|
|
func (g *generator) createGenInterface(typeDef *winmd.TypeDef, requiresActivation bool) (*genInterface, error) {
|
|
funcs, err := g.getGenFuncs(typeDef, requiresActivation)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Interfaces' TypeDef rows must have a GuidAttribute as well as a VersionAttribute.
|
|
guid, err := typeDef.GUID()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
typeSig, err := g.Signature(typeDef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &genInterface{
|
|
Name: typeDefGoName(typeDef.TypeName, typeDef.Flags.Public()),
|
|
GUID: guid,
|
|
Signature: typeSig,
|
|
Funcs: funcs,
|
|
}, nil
|
|
}
|
|
|
|
// https://docs.microsoft.com/en-us/uwp/winrt-cref/winmd-files#runtime-classes
|
|
func (g *generator) createGenClass(typeDef *winmd.TypeDef) (*genClass, error) {
|
|
var requiredImports []*genImport
|
|
var exclusiveInterfaceTypes []*winmd.TypeDef
|
|
|
|
// true => interface requires activation, false => interface is implemented by this class
|
|
activatedInterfaces := make(map[string]bool)
|
|
|
|
// get all the interfaces this class implements
|
|
interfaces, err := typeDef.GetImplementedInterfaces()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
implInterfaces := make([]*genInterface, 0, len(interfaces))
|
|
for _, iface := range interfaces {
|
|
// the interface needs to be implemented by this class
|
|
requiredImports = append(requiredImports, &genImport{iface.Namespace, iface.Name})
|
|
|
|
ifaceTypeDef, err := g.mdStore.TypeDefByName(iface.Namespace + "." + iface.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
itf, err := g.createGenInterface(ifaceTypeDef, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pkg := ""
|
|
if typeDef.TypeNamespace != ifaceTypeDef.TypeNamespace {
|
|
pkg = typePackage(iface.Namespace, iface.Name)
|
|
}
|
|
for _, f := range itf.Funcs {
|
|
f.InheritedFrom = winmd.QualifiedID{
|
|
Namespace: pkg,
|
|
Name: typeDefGoName(ifaceTypeDef.TypeName, ifaceTypeDef.Flags.Public()),
|
|
}
|
|
}
|
|
|
|
implInterfaces = append(implInterfaces, itf)
|
|
|
|
// The interface we implement may be exclusive to this class, in which case we need to generate it.
|
|
// An exclusive (private) interface should always belong to the same winmd file. So even if this is
|
|
// a TypeRef, the class should be found using its name.
|
|
if td, err := g.mdStore.TypeDefByName(iface.Namespace + "." + iface.Name); err == nil {
|
|
if _, ok := g.interfaceIsExclusiveTo(td); ok {
|
|
exclusiveInterfaceTypes = append(exclusiveInterfaceTypes, td)
|
|
activatedInterfaces[td.TypeNamespace+"."+td.TypeName] = false // implemented interfaces do not require activation
|
|
}
|
|
}
|
|
}
|
|
|
|
// Runtime classes have zero or more StaticAttribute custom attributes
|
|
// https://docs.microsoft.com/en-us/uwp/winrt-cref/winmd-files#static-interfaces
|
|
staticAttributeBlobs := typeDef.GetTypeDefAttributesWithType(winmd.AttributeTypeStaticAttribute)
|
|
for _, blob := range staticAttributeBlobs {
|
|
class := extractClassFromBlob(blob)
|
|
_ = level.Debug(g.logger).Log("msg", "found static interface", "class", class)
|
|
staticClass, err := g.mdStore.TypeDefByName(class)
|
|
if err != nil {
|
|
_ = level.Error(g.logger).Log("msg", "static class defined in StaticAttribute not found", "class", class, "err", err)
|
|
return nil, err
|
|
}
|
|
|
|
exclusiveInterfaceTypes = append(exclusiveInterfaceTypes, staticClass)
|
|
activatedInterfaces[staticClass.TypeNamespace+"."+staticClass.TypeName] = true // static interfaces require activation
|
|
}
|
|
|
|
// Runtime classes have zero or more ActivatableAttribute custom attributes
|
|
// https://docs.microsoft.com/en-us/uwp/winrt-cref/winmd-files#activation
|
|
activatableAttributeBlobs := typeDef.GetTypeDefAttributesWithType(winmd.AttributeTypeActivatableAttribute)
|
|
hasEmptyConstructor := false
|
|
for _, blob := range activatableAttributeBlobs {
|
|
// check for empty constructor
|
|
if activatableAttrIsEmpty(blob) {
|
|
// this activatable attribute is empty, so the class has an empty constructor
|
|
hasEmptyConstructor = true
|
|
continue
|
|
}
|
|
|
|
// check for an activation interface
|
|
class := extractClassFromBlob(blob)
|
|
_ = level.Debug(g.logger).Log("msg", "found activatable interface", "class", class)
|
|
activatableClass, err := g.mdStore.TypeDefByName(class)
|
|
if err != nil {
|
|
// the activatable class may be empty in some cases, example:
|
|
// https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.14393.0/winrt/windows.devices.bluetooth.advertisement.idl#L518
|
|
_ = level.Error(g.logger).Log("msg", "activatable class defined in ActivatableAttribute not found", "class", class, "err", err)
|
|
|
|
// so do not fail
|
|
continue
|
|
}
|
|
exclusiveInterfaceTypes = append(exclusiveInterfaceTypes, activatableClass)
|
|
activatedInterfaces[activatableClass.TypeNamespace+"."+activatableClass.TypeName] = true // activatable interfaces require activation
|
|
}
|
|
|
|
// generate exclusive interfaces
|
|
var exclusiveGenInterfaces []*genInterface
|
|
for _, iface := range exclusiveInterfaceTypes {
|
|
requiresActivation := activatedInterfaces[iface.TypeNamespace+"."+iface.TypeName]
|
|
isExtendedInterface := !requiresActivation
|
|
|
|
ifaceGen, err := g.createGenInterface(iface, requiresActivation)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// if all methods from the exclusive interface have been filtered, and the interface
|
|
// is an activated interface, then we can skip it.
|
|
impl := false
|
|
for _, m := range ifaceGen.Funcs {
|
|
if m.Implement {
|
|
impl = true
|
|
}
|
|
}
|
|
|
|
// Extended interfaces always need to be generated (even if they have no method).
|
|
if isExtendedInterface || impl {
|
|
exclusiveGenInterfaces = append(exclusiveGenInterfaces, ifaceGen)
|
|
}
|
|
}
|
|
|
|
typeSig, err := g.Signature(typeDef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &genClass{
|
|
Name: typeDefGoName(typeDef.TypeName, typeDef.Flags.Public()),
|
|
Signature: typeSig,
|
|
RequiresImports: requiredImports,
|
|
FullyQualifiedName: typeDef.TypeNamespace + "." + typeDef.TypeName,
|
|
ImplInterfaces: implInterfaces,
|
|
ExclusiveInterfaces: exclusiveGenInterfaces,
|
|
HasEmptyConstructor: hasEmptyConstructor,
|
|
IsAbstract: typeDef.Flags.Abstract(),
|
|
}, nil
|
|
}
|
|
|
|
// https://docs.microsoft.com/en-us/uwp/winrt-cref/winmd-files#enums
|
|
func (g *generator) createGenEnum(typeDef *winmd.TypeDef) (*genEnum, error) {
|
|
fields, err := typeDef.ResolveFieldList(typeDef.Ctx())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// An enum has a single instance field that specifies the underlying integer type for the enum,
|
|
// as well as zero or more static fields; one for each enum value defined by the enum type.
|
|
if len(fields) == 0 {
|
|
return nil, fmt.Errorf("enum %s has no fields", typeDef.TypeName)
|
|
}
|
|
|
|
// the first row should be the underlying integer type of the enum. It must have the following flags:
|
|
if !(fields[0].Flags.Private() && fields[0].Flags.SpecialName() && fields[0].Flags.RTSpecialName()) {
|
|
return nil, fmt.Errorf("enum %s has more than one instance field, expected 1", typeDef.TypeNamespace+"."+typeDef.TypeName)
|
|
}
|
|
|
|
fieldSig, err := fields[0].Signature.Reader().Field(typeDef.Ctx())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
elType, err := g.elementType(typeDef.Ctx(), fieldSig.Field)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// this will always be a primitive type, so we can just use the name
|
|
enumType := elType.name
|
|
|
|
// After the enum value definition comes a field definition for each of the values in the enumeration.
|
|
var enumValues []*genEnumValue
|
|
for i, field := range fields[1:] {
|
|
if !(field.Flags.Public() && field.Flags.Static() && field.Flags.Literal() && field.Flags.HasDefault()) {
|
|
return nil,
|
|
fmt.Errorf(
|
|
"enum %s field value does not comply with the spec. Checkout https://docs.microsoft.com/en-us/uwp/winrt-cref/winmd-files#enums",
|
|
typeDef.TypeNamespace+"."+typeDef.TypeName,
|
|
)
|
|
}
|
|
|
|
var fieldIndex uint32 = typeDef.FieldList.Start() + 1 + uint32(i)
|
|
enumRawValue, err := typeDef.GetValueForEnumField(fieldIndex)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
enumValues = append(enumValues, &genEnumValue{
|
|
Name: enumName(typeDef.TypeName, field.Name),
|
|
Value: enumRawValue,
|
|
})
|
|
}
|
|
|
|
typeSig, err := g.Signature(typeDef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &genEnum{
|
|
Name: typeDefGoName(typeDef.TypeName, typeDef.Flags.Public()),
|
|
Type: enumType,
|
|
Signature: typeSig,
|
|
Values: enumValues,
|
|
}, nil
|
|
}
|
|
|
|
// https://docs.microsoft.com/en-us/uwp/winrt-cref/winmd-files#structs
|
|
func (g *generator) createGenStruct(typeDef *winmd.TypeDef) (*genStruct, error) {
|
|
// structs do not have methods, only fields
|
|
fields, err := typeDef.ResolveFieldList(typeDef.Ctx())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
curPkg := typePackage(typeDef.TypeNamespace, typeDef.TypeName)
|
|
|
|
var genFields []*genParam
|
|
for _, f := range fields {
|
|
fSig, err := f.Signature.Reader().Field(typeDef.Ctx())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fieldType, err := g.elementType(typeDef.Ctx(), fSig.Field)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Struct fields must be fundamental types, enums, or other structs
|
|
genFields = append(genFields, &genParam{
|
|
callerPackage: curPkg,
|
|
varName: cleanReservedWords(f.Name),
|
|
IsOut: false,
|
|
Type: fieldType,
|
|
})
|
|
}
|
|
|
|
typeSig, err := g.Signature(typeDef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &genStruct{
|
|
Name: typeDefGoName(typeDef.TypeName, typeDef.Flags.Public()),
|
|
Signature: typeSig,
|
|
Fields: genFields,
|
|
}, nil
|
|
}
|
|
|
|
// https://docs.microsoft.com/en-us/uwp/winrt-cref/winmd-files#delegates
|
|
func (g *generator) createGenDelegate(typeDef *winmd.TypeDef) (*genDelegate, error) {
|
|
// FieldList: must be empty
|
|
// MethodList: An index into the MethodDef table (ECMA II.22.26), marking the first of a contiguous run of methods owned by this type.
|
|
// Delegates' TypeDef rows must have a GuidAttribute
|
|
guid, err := typeDef.GUID()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Delegates have exactly two MethodDef table entries. The first defines a constructor.
|
|
methods, err := typeDef.ResolveMethodList(typeDef.Ctx())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(methods) != 2 {
|
|
return nil, fmt.Errorf("delegate %s has more than two methods", typeDef.TypeNamespace+"."+typeDef.TypeName)
|
|
}
|
|
|
|
// This constructor is a compatibility marker. WinRT Delegates have no such constructor method.
|
|
|
|
// We only care about the invoke method
|
|
invokeMethod := methods[1]
|
|
if invokeMethod.Name != invokeMethodName {
|
|
return nil, fmt.Errorf("found method '%s' on delegate %s but expected '%s'",
|
|
invokeMethod.Name,
|
|
typeDef.TypeNamespace+"."+typeDef.TypeName,
|
|
invokeMethodName,
|
|
)
|
|
}
|
|
|
|
// this is going to be used to define the callback type. We don't
|
|
// really need the whole function, only its input parameters,
|
|
// so we can reuse the logic used for getting them.
|
|
f, err := g.genFuncFromMethod(typeDef, &invokeMethod, "", false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
typeSig, err := g.Signature(typeDef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &genDelegate{
|
|
Name: typeDefGoName(typeDef.TypeName, true),
|
|
GUID: guid,
|
|
Signature: typeSig,
|
|
InParams: f.InParams,
|
|
}, nil
|
|
}
|
|
|
|
func (g *generator) interfaceIsExclusiveTo(typeDef *winmd.TypeDef) (string, bool) {
|
|
exclusiveToBlob, err := typeDef.GetAttributeWithType(winmd.AttributeTypeExclusiveTo)
|
|
// an error here is fine, we just won't have the ExclusiveTo attribute
|
|
if err != nil {
|
|
return "", false
|
|
}
|
|
exclusiveToClass := extractClassFromBlob(exclusiveToBlob)
|
|
return exclusiveToClass, true
|
|
}
|
|
|
|
func activatableAttrIsEmpty(blob []byte) bool {
|
|
// the activatable attribute is empty if the size is 0
|
|
// 01 00 - header
|
|
// 00 - size
|
|
return len(blob) >= 3 && blob[0] == 0x01 && blob[1] == 0x00 && blob[2] == 0x00
|
|
}
|
|
|
|
func extractClassFromBlob(blob []byte) string {
|
|
// the blob contains a two byte header
|
|
// 01 00
|
|
// followed by a byte with the size
|
|
// XX
|
|
// followed by the type name
|
|
|
|
// so we need at least 4 bytes
|
|
if len(blob) < 4 {
|
|
return ""
|
|
}
|
|
size := blob[2]
|
|
class := blob[3 : 3+size]
|
|
return string(class)
|
|
}
|
|
|
|
func (g *generator) getGenFuncs(typeDef *winmd.TypeDef, requiresActivation bool) ([]*genFunc, error) {
|
|
var genFuncs []*genFunc
|
|
|
|
methods, err := typeDef.ResolveMethodList(typeDef.Ctx())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var exclusiveToType string
|
|
if ex, ok := g.interfaceIsExclusiveTo(typeDef); ok {
|
|
exclusiveToType = ex
|
|
}
|
|
|
|
for _, m := range methods {
|
|
methodDef := m
|
|
generatedFunc, err := g.genFuncFromMethod(typeDef, &methodDef, exclusiveToType, requiresActivation)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
genFuncs = append(genFuncs, generatedFunc)
|
|
}
|
|
|
|
return genFuncs, nil
|
|
}
|
|
|
|
func (g *generator) genFuncFromMethod(typeDef *winmd.TypeDef, methodDef *types.MethodDef, exclusiveTo string, requiresActivation bool) (*genFunc, error) {
|
|
// add the type imports to the top of the file
|
|
// only if the method is going to be implemented
|
|
|
|
overloadName := winmd.GetMethodOverloadName(typeDef.Ctx(), methodDef)
|
|
implement := g.shouldImplementMethod(overloadName)
|
|
if !implement {
|
|
// if we don't implement the method, we don't need to gather
|
|
// all the information, just the name of it is enough
|
|
return &genFunc{
|
|
Name: overloadName,
|
|
RequiresImports: nil,
|
|
Implement: implement,
|
|
InParams: nil,
|
|
ReturnParams: nil,
|
|
FuncOwner: typeDefGoName(typeDef.TypeName, typeDef.Flags.Public()),
|
|
ExclusiveTo: exclusiveTo,
|
|
RequiresActivation: requiresActivation,
|
|
}, nil
|
|
}
|
|
|
|
curPackage := typePackage(typeDef.TypeNamespace, typeDef.TypeName)
|
|
|
|
params, err := g.getInParameters(curPackage, typeDef, methodDef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
retParams, err := g.getReturnParameters(curPackage, typeDef, methodDef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// iterate over all parameters (in or out) to gather the required imports
|
|
// and calculate if we need the package name when referencing a type
|
|
// based on the current package
|
|
var allImplementedParams []*genParam
|
|
allImplementedParams = append(allImplementedParams, params...)
|
|
allImplementedParams = append(allImplementedParams, retParams...)
|
|
|
|
var requiredImports []*genImport
|
|
for _, p := range allImplementedParams {
|
|
p.callerPackage = curPackage
|
|
if !p.Type.IsPrimitive {
|
|
requiredImports = append(requiredImports, &genImport{p.Type.namespace, p.Type.name})
|
|
}
|
|
}
|
|
|
|
return &genFunc{
|
|
Name: overloadName,
|
|
RequiresImports: requiredImports,
|
|
Implement: implement,
|
|
InParams: params,
|
|
ReturnParams: retParams,
|
|
FuncOwner: typeDefGoName(typeDef.TypeName, typeDef.Flags.Public()),
|
|
ExclusiveTo: exclusiveTo,
|
|
RequiresActivation: requiresActivation,
|
|
}, nil
|
|
}
|
|
|
|
func (g *generator) shouldImplementMethod(methodName string) bool {
|
|
return g.methodFilter.Filter(methodName)
|
|
}
|
|
|
|
func (g *generator) getInParameters(curPackage string, typeDef *winmd.TypeDef, methodDef *types.MethodDef) ([]*genParam, error) {
|
|
|
|
params, err := methodDef.ResolveParamList(typeDef.Ctx())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// the signature contains the parameter
|
|
// types and return type of the method
|
|
r := methodDef.Signature.Reader()
|
|
mr, err := r.Method(typeDef.Ctx())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var genParams []*genParam
|
|
for i, e := range mr.Params {
|
|
param := getParamByIndex(params, uint16(i+1))
|
|
if param == nil {
|
|
_ = level.Error(g.logger).Log("msg", "Parameter with index not found", "index", i+1)
|
|
continue // do not fail
|
|
}
|
|
|
|
// When encoding an Array parameter for any interface member type, the array length
|
|
// parameter that immediately precedes the array parameter is omitted from both the
|
|
// MethodDefSig blob as well from as the params table. => so we need to add it manually.
|
|
// Do not trust e.IsArray variable, it's only true for the ELEMENT_TYPE_ARRAY, it
|
|
if e.Type.Kind == types.ELEMENT_TYPE_SZARRAY || e.Type.Kind == types.ELEMENT_TYPE_ARRAY {
|
|
// The direction of the array parameter is directly encoded in metadata.The direction of
|
|
// the array length parameter may be inferred as follows.
|
|
// - If the array parameter is an in parameter, the array length parameter must also
|
|
// be an IN PARAMETER.
|
|
// - If the array parameter is an out parameter and is not carrying the BYREF
|
|
// marker, the array length is an IN PARAMETER.
|
|
|
|
// - If the array parameter is an out parameter and carries the BYREF marker, the
|
|
// array length is an OUT PARAMETER.
|
|
sizeIsOutParam := param.Flags.Out() && e.ByRef
|
|
genParams = append(genParams, &genParam{
|
|
callerPackage: curPackage,
|
|
// Do not change this without also changing the code in the templates
|
|
varName: cleanReservedWords(param.Name + "Size"),
|
|
IsOut: sizeIsOutParam,
|
|
Type: &genParamType{
|
|
namespace: "",
|
|
name: "uint32",
|
|
defaultValue: genDefaultValue{"0", true},
|
|
IsPrimitive: true,
|
|
IsPointer: false,
|
|
IsArray: false,
|
|
},
|
|
})
|
|
}
|
|
|
|
elType, err := g.elementType(typeDef.Ctx(), e)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
genParams = append(genParams, &genParam{
|
|
callerPackage: curPackage,
|
|
varName: cleanReservedWords(getParamName(params, uint16(i+1))),
|
|
IsOut: param.Flags.Out(),
|
|
Type: elType,
|
|
})
|
|
}
|
|
|
|
return genParams, nil
|
|
}
|
|
|
|
func (g *generator) getReturnParameters(curPackage string, typeDef *winmd.TypeDef, methodDef *types.MethodDef) ([]*genParam, error) {
|
|
// the signature contains the parameter
|
|
// types and return type of the method
|
|
r := methodDef.Signature.Reader()
|
|
methodSignature, err := r.Method(typeDef.Ctx())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var genParams []*genParam
|
|
|
|
// ignore void types
|
|
if methodSignature.Return.Type.Kind == types.ELEMENT_TYPE_VOID {
|
|
return genParams, nil
|
|
}
|
|
|
|
elType, err := g.elementType(typeDef.Ctx(), methodSignature.Return)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
genParams = append(genParams, &genParam{
|
|
// return param always has an index of zero
|
|
callerPackage: curPackage,
|
|
varName: "out",
|
|
IsOut: true,
|
|
Type: elType,
|
|
})
|
|
|
|
return genParams, nil
|
|
}
|
|
|
|
func getParamName(params []types.Param, i uint16) string {
|
|
for _, p := range params {
|
|
if p.Sequence == i {
|
|
return p.Name
|
|
}
|
|
}
|
|
return fmt.Sprintf("__ERROR_PARAM_%d_NOT_FOUND__", i)
|
|
}
|
|
|
|
func getParamByIndex(params []types.Param, i uint16) *types.Param {
|
|
for _, p := range params {
|
|
if p.Sequence == i {
|
|
return &p
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (g *generator) elementType(ctx *types.Context, e types.Element) (*genParamType, error) {
|
|
switch e.Type.Kind {
|
|
case types.ELEMENT_TYPE_BOOLEAN:
|
|
return &genParamType{
|
|
namespace: "",
|
|
name: "bool",
|
|
IsPointer: false,
|
|
IsPrimitive: true,
|
|
IsArray: false,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_CHAR:
|
|
return &genParamType{
|
|
namespace: "",
|
|
name: "byte",
|
|
IsPointer: false,
|
|
IsPrimitive: true,
|
|
IsArray: false,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_I1:
|
|
return &genParamType{
|
|
namespace: "",
|
|
name: "int8",
|
|
IsPointer: false,
|
|
IsPrimitive: true,
|
|
IsArray: false,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_U1:
|
|
return &genParamType{
|
|
namespace: "",
|
|
name: "uint8",
|
|
IsPointer: false,
|
|
IsPrimitive: true,
|
|
IsArray: false,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_I2:
|
|
return &genParamType{
|
|
namespace: "",
|
|
name: "int16",
|
|
IsPointer: false,
|
|
IsPrimitive: true,
|
|
IsArray: false,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_U2:
|
|
return &genParamType{
|
|
namespace: "",
|
|
name: "uint16",
|
|
IsPointer: false,
|
|
IsPrimitive: true,
|
|
IsArray: false,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_I4:
|
|
return &genParamType{
|
|
namespace: "",
|
|
name: "int32",
|
|
IsPointer: false,
|
|
IsPrimitive: true,
|
|
IsArray: false,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_U4:
|
|
return &genParamType{
|
|
namespace: "",
|
|
name: "uint32",
|
|
IsPointer: false,
|
|
IsPrimitive: true,
|
|
IsArray: false,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_I8:
|
|
return &genParamType{
|
|
namespace: "",
|
|
name: "int64",
|
|
IsPointer: false,
|
|
IsPrimitive: true,
|
|
IsArray: false,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_U8:
|
|
return &genParamType{
|
|
namespace: "",
|
|
name: "uint64",
|
|
IsPointer: false,
|
|
IsPrimitive: true,
|
|
IsArray: false,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_R4:
|
|
return &genParamType{
|
|
namespace: "",
|
|
name: "float32",
|
|
IsPointer: false,
|
|
IsPrimitive: true,
|
|
IsArray: false,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_R8:
|
|
return &genParamType{
|
|
namespace: "",
|
|
name: "float64",
|
|
IsPointer: false,
|
|
IsPrimitive: true,
|
|
IsArray: false,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_STRING:
|
|
return &genParamType{
|
|
namespace: "",
|
|
name: "string",
|
|
IsPointer: false,
|
|
IsPrimitive: true,
|
|
IsArray: false,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_GENERICINST:
|
|
fallthrough
|
|
case types.ELEMENT_TYPE_CLASS:
|
|
// return class name
|
|
namespace, name, err := ctx.ResolveTypeDefOrRefName(e.Type.TypeDef.Index)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &genParamType{
|
|
namespace: namespace,
|
|
name: name,
|
|
IsPointer: true,
|
|
IsPrimitive: false,
|
|
IsArray: false,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_VALUETYPE:
|
|
namespace, name, err := ctx.ResolveTypeDefOrRefName(e.Type.TypeDef.Index)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Check for system types
|
|
if t, ok := isSystemType(namespace, name); ok {
|
|
return t, nil
|
|
}
|
|
|
|
elementTypeDef, err := g.mdStore.TypeDefByName(namespace + "." + name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// if its an enum, we will need the underlying type
|
|
isEnum := false
|
|
enumType := ""
|
|
if elementTypeDef.IsEnum() {
|
|
enumData, err := g.createGenEnum(elementTypeDef)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Treat the enum as a primitive
|
|
isEnum = true
|
|
enumType = enumData.Type
|
|
}
|
|
return &genParamType{
|
|
namespace: namespace,
|
|
name: name,
|
|
IsPointer: false,
|
|
IsPrimitive: false,
|
|
IsArray: false,
|
|
IsEnum: isEnum,
|
|
UnderlyingEnumType: enumType,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_VAR:
|
|
// Generic types are not fully supported yet,
|
|
// so we will just pass the raw unsafe.Pointer up to the user.
|
|
return &genParamType{
|
|
namespace: "unsafe",
|
|
name: "Pointer",
|
|
IsGeneric: true,
|
|
IsPointer: false,
|
|
IsPrimitive: false,
|
|
IsArray: false,
|
|
defaultValue: g.elementDefaultValue(ctx, e),
|
|
}, nil
|
|
case types.ELEMENT_TYPE_SZARRAY:
|
|
//A single-dimensional, zero lower-bound array type modifier
|
|
|
|
// e.Type.SZArray.Elem should be non-nil
|
|
param, err := g.elementType(ctx, *e.Type.SZArray.Elem)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
param.IsArray = true
|
|
// override default val
|
|
param.defaultValue = genDefaultValue{"nil", true}
|
|
|
|
return param, err
|
|
case types.ELEMENT_TYPE_OBJECT:
|
|
// This represents System.Object, so just use a pointer
|
|
return &genParamType{
|
|
namespace: "unsafe",
|
|
name: "Pointer",
|
|
IsPointer: false,
|
|
IsPrimitive: false,
|
|
IsArray: false,
|
|
defaultValue: genDefaultValue{"nil", true},
|
|
}, nil
|
|
default:
|
|
return nil, fmt.Errorf("unsupported element type: %v", e.Type.Kind)
|
|
}
|
|
}
|
|
|
|
func isSystemType(namespace, name string) (*genParamType, bool) {
|
|
if namespace != "System" {
|
|
return nil, false
|
|
}
|
|
switch name {
|
|
case "Guid":
|
|
// System.Guid is a struct (value type), so we can just
|
|
// use syscall.GUID which has the same structure
|
|
return &genParamType{
|
|
namespace: "syscall",
|
|
name: "GUID",
|
|
IsPointer: false,
|
|
IsPrimitive: false,
|
|
IsArray: false,
|
|
IsEnum: false,
|
|
defaultValue: genDefaultValue{value: "GUID{}", isPrimitive: false},
|
|
}, true
|
|
}
|
|
|
|
return nil, false
|
|
}
|
|
|
|
func (g *generator) elementDefaultValue(ctx *types.Context, e types.Element) genDefaultValue {
|
|
switch e.Type.Kind {
|
|
case types.ELEMENT_TYPE_BOOLEAN:
|
|
return genDefaultValue{"false", true}
|
|
case types.ELEMENT_TYPE_CHAR,
|
|
types.ELEMENT_TYPE_I1, types.ELEMENT_TYPE_U1,
|
|
types.ELEMENT_TYPE_I2, types.ELEMENT_TYPE_U2,
|
|
types.ELEMENT_TYPE_I4, types.ELEMENT_TYPE_U4,
|
|
types.ELEMENT_TYPE_I8, types.ELEMENT_TYPE_U8:
|
|
return genDefaultValue{"0", true}
|
|
case types.ELEMENT_TYPE_R4, types.ELEMENT_TYPE_R8:
|
|
return genDefaultValue{"0.0", true}
|
|
case types.ELEMENT_TYPE_STRING:
|
|
return genDefaultValue{"\"\"", true}
|
|
case types.ELEMENT_TYPE_CLASS,
|
|
types.ELEMENT_TYPE_GENERICINST, types.ELEMENT_TYPE_SZARRAY:
|
|
return genDefaultValue{"nil", true}
|
|
case types.ELEMENT_TYPE_VALUETYPE:
|
|
// we need to get the underlying type (enum, struct, etc...)
|
|
namespace, name, err := ctx.ResolveTypeDefOrRefName(e.Type.TypeDef.Index)
|
|
if err != nil {
|
|
return genDefaultValue{"__ERROR_" + err.Error(), true}
|
|
}
|
|
elementTypeDef, err := g.mdStore.TypeDefByName(namespace + "." + name)
|
|
if err != nil {
|
|
return genDefaultValue{"__ERROR_" + err.Error(), true}
|
|
}
|
|
|
|
if elementTypeDef.IsEnum() {
|
|
// return the first enum value
|
|
fields, err := elementTypeDef.ResolveFieldList(ctx)
|
|
if err != nil {
|
|
return genDefaultValue{"__ERROR_" + err.Error(), true}
|
|
}
|
|
// the first field defines the enum type, the second is the first value
|
|
if len(fields) < 2 {
|
|
return genDefaultValue{"__ERROR_" + fmt.Errorf("enum %v has no fields", namespace+"."+name).Error(), true}
|
|
}
|
|
|
|
return genDefaultValue{enumName(elementTypeDef.TypeName, fields[1].Name), false}
|
|
} else if elementTypeDef.IsStruct() {
|
|
return genDefaultValue{elementTypeDef.TypeName + "{}", false}
|
|
}
|
|
|
|
return genDefaultValue{"nil", true}
|
|
case types.ELEMENT_TYPE_VAR:
|
|
return genDefaultValue{"nil", true}
|
|
case types.ELEMENT_TYPE_OBJECT:
|
|
return genDefaultValue{"nil", true}
|
|
default:
|
|
return genDefaultValue{"__ERROR_" + fmt.Errorf("unsupported element type: %v", e.Type.Kind).Error(), true}
|
|
}
|
|
}
|
|
|
|
func (g *generator) Signature(typeDef *winmd.TypeDef) (string, error) {
|
|
// Signature generation defined in
|
|
// https://docs.microsoft.com/en-us/uwp/winrt-cref/winrt-type-system#guid-generation-for-parameterized-types
|
|
|
|
// type_signature => (only relevant types included)
|
|
// - interface_signature
|
|
// - delegate_signature
|
|
// - interface_group_signature
|
|
// - runtime_class_signature
|
|
// - struct_signature
|
|
// - enum_signature
|
|
// # these instance signatures cannot be generated until instantiated
|
|
// - pinterface_instance_signature
|
|
// - pdelegate_instance_signature
|
|
//
|
|
// Where:
|
|
// - interface_signature => guid
|
|
// - delegate_signature => "delegate(" guid ")"
|
|
// - interface_group_signature => "ig(" interface_group_name ";" default_interface ")"
|
|
// - runtime_class_signature => "rc(" runtime_class_name ";" default_interface ")"
|
|
// - struct_signature => "struct(" struct_name ";" args ")"
|
|
// - enum_signature => "enum(" enum_name ";" enum_underlying_type ")"
|
|
|
|
switch {
|
|
case typeDef.IsInterface():
|
|
// interface_signature => guid
|
|
guid, err := typeDef.GUID()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return fmt.Sprintf("{%s}", guid), nil
|
|
case typeDef.IsEnum():
|
|
// enum_signature => "enum(" enum_name ";" enum_underlying_type ")"
|
|
// the first field should be the underlying integer type of the enum. It must have the following flags:
|
|
fields, err := typeDef.ResolveFieldList(typeDef.Ctx())
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
fieldSig, err := fields[0].Signature.Reader().Field(typeDef.Ctx())
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
enumType := primitiveTypeSignature(fieldSig.Field.Type.Kind)
|
|
return fmt.Sprintf(`enum(%s;%s)`, typeDef.TypeNamespace+"."+typeDef.TypeName, enumType), nil
|
|
case typeDef.IsStruct():
|
|
// struct_signature => "struct(" struct_name ";" args ")"
|
|
fields, err := typeDef.ResolveFieldList(typeDef.Ctx())
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
structArgs := []string{}
|
|
for _, f := range fields {
|
|
fSig, err := f.Signature.Reader().Field(typeDef.Ctx())
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Struct fields must be fundamental types, enums, or other structs
|
|
if fSig.Field.Type.Kind == types.ELEMENT_TYPE_VALUETYPE {
|
|
// this is an struct or an enum
|
|
fieldType, err := g.elementType(typeDef.Ctx(), fSig.Field)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
t, err := g.mdStore.TypeDefByName(fieldType.namespace + "." + fieldType.name)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
sig, err := g.Signature(t)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
structArgs = append(structArgs, sig)
|
|
} else {
|
|
// Assume everything else is a fundamental type
|
|
structArgs = append(structArgs, primitiveTypeSignature(fSig.Field.Type.Kind))
|
|
}
|
|
}
|
|
return fmt.Sprintf(`struct(%s;%s)`, typeDef.TypeNamespace+"."+typeDef.TypeName, strings.Join(structArgs, ";")), nil
|
|
case typeDef.IsDelegate():
|
|
//delegate_signature => "delegate(" guid ")"
|
|
guid, err := typeDef.GUID()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return fmt.Sprintf(`delegate({%s})`, guid), nil
|
|
case typeDef.IsRuntimeClass():
|
|
// Static only classes carry the abstract flag.
|
|
// These cannot be instantiated so no signature needed.
|
|
if typeDef.Flags.Abstract() {
|
|
return "", nil
|
|
}
|
|
|
|
// runtime_class_signature => "rc(" runtime_class_name ";" default_interface ")"
|
|
|
|
// Runtime classes must specify the DefaultAttribute on exactly one of their InterfaceImpl rows.
|
|
defaultInterface, err := typeDef.GetAttributeWithType(winmd.AttributeTypeDefaultAttribute)
|
|
if err != nil {
|
|
// Some classes (Windows.Devices.Bluetooth.Advertisement.BluetoothLEAdvertisementWatcher) do not
|
|
// define a runtime class. I'm not sure if this is an error in the IDL or the documentation.
|
|
// But we are not going to fail here. Just default to the first implemented interface
|
|
ifs, ifserr := typeDef.GetImplementedInterfaces()
|
|
if ifserr != nil {
|
|
return "", err
|
|
}
|
|
|
|
if len(ifs) == 0 {
|
|
return "", err
|
|
}
|
|
defaultInterface = []byte(ifs[0].Namespace + "." + ifs[0].Name)
|
|
}
|
|
|
|
td, err := g.mdStore.TypeDefByName(string(defaultInterface))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
defaultInterfaceSignature, err := g.Signature(td)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return fmt.Sprintf(`rc(%s;%s)`, typeDef.TypeNamespace+"."+typeDef.TypeName, defaultInterfaceSignature), nil
|
|
default:
|
|
return "", fmt.Errorf("unsupported type: %v", typeDef.TypeName)
|
|
}
|
|
}
|
|
|
|
func primitiveTypeSignature(kind types.ElementTypeKind) string {
|
|
switch kind {
|
|
// Fundamental types
|
|
case types.ELEMENT_TYPE_U1:
|
|
return winrt.SignatureUInt8
|
|
case types.ELEMENT_TYPE_U2:
|
|
return winrt.SignatureUInt16
|
|
case types.ELEMENT_TYPE_U4:
|
|
return winrt.SignatureUInt32
|
|
case types.ELEMENT_TYPE_U8:
|
|
return winrt.SignatureUInt64
|
|
case types.ELEMENT_TYPE_I1:
|
|
return winrt.SignatureInt8
|
|
case types.ELEMENT_TYPE_I2:
|
|
return winrt.SignatureInt16
|
|
case types.ELEMENT_TYPE_I4:
|
|
return winrt.SignatureInt32
|
|
case types.ELEMENT_TYPE_I8:
|
|
return winrt.SignatureInt64
|
|
case types.ELEMENT_TYPE_R4:
|
|
return winrt.SignatureFloat32
|
|
case types.ELEMENT_TYPE_R8:
|
|
return winrt.SignatureFloat64
|
|
case types.ELEMENT_TYPE_BOOLEAN:
|
|
return winrt.SignatureBool
|
|
case types.ELEMENT_TYPE_CHAR:
|
|
return winrt.SignatureChar
|
|
case types.ELEMENT_TYPE_STRING:
|
|
return winrt.SignatureString
|
|
}
|
|
return ""
|
|
}
|