mirror of
https://github.com/FabInfra/capnproto-dotnetcore_Runtime.git
synced 2025-03-12 14:51:41 +01:00
Fix attempted use of incomplete dependent types in schema model.
Resolve node hierarchy and names in a new 1st pass before generating types in pass2. Consider imported files.
This commit is contained in:
parent
a0fcb93676
commit
92a7c03cb2
@ -9,19 +9,18 @@ namespace CapnpC.Model
|
|||||||
class SchemaModel
|
class SchemaModel
|
||||||
{
|
{
|
||||||
readonly Schema.CodeGeneratorRequest.Reader _request;
|
readonly Schema.CodeGeneratorRequest.Reader _request;
|
||||||
readonly List<GenFile> _files = new List<GenFile>();
|
readonly List<GenFile> _generatedFiles = new List<GenFile>();
|
||||||
readonly Stack<IHasNestedDefinitions> _typeNest = new Stack<IHasNestedDefinitions>();
|
Dictionary<ulong, IHasNestedDefinitions> _allDefinitions = new Dictionary<ulong, IHasNestedDefinitions>();
|
||||||
readonly TypeDefinitionManager _typeDefMgr = new TypeDefinitionManager();
|
readonly TypeDefinitionManager _typeDefMgr = new TypeDefinitionManager();
|
||||||
Method _currentMethod;
|
|
||||||
|
|
||||||
Dictionary<ulong, Schema.Node.Reader> _id2node;
|
readonly Dictionary<ulong, Schema.Node.Reader> _id2node = new Dictionary<ulong, Schema.Node.Reader>();
|
||||||
|
|
||||||
public SchemaModel(Schema.CodeGeneratorRequest.Reader request)
|
public SchemaModel(Schema.CodeGeneratorRequest.Reader request)
|
||||||
{
|
{
|
||||||
_request = request;
|
_request = request;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<GenFile> FilesToGenerate => _files;
|
public IReadOnlyList<GenFile> FilesToGenerate => _generatedFiles;
|
||||||
|
|
||||||
Schema.Node.Reader IdToNode(ulong id)
|
Schema.Node.Reader IdToNode(ulong id)
|
||||||
{
|
{
|
||||||
@ -42,69 +41,196 @@ namespace CapnpC.Model
|
|||||||
throw new InvalidSchemaException("No nodes, nothing to generate");
|
throw new InvalidSchemaException("No nodes, nothing to generate");
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
foreach (var node in _request.Nodes)
|
||||||
{
|
{
|
||||||
_id2node = _request.Nodes.ToDictionary(n => n.Id);
|
if (_id2node.TryGetValue(node.Id, out var existingNode))
|
||||||
}
|
|
||||||
catch (ArgumentException)
|
|
||||||
{
|
|
||||||
throw new InvalidSchemaException("Nodes with duplicate IDs detected");
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var reqFile in _request.RequestedFiles)
|
|
||||||
{
|
|
||||||
var file = new GenFile()
|
|
||||||
{
|
{
|
||||||
Name = reqFile.Filename
|
throw new InvalidSchemaException($"Node {node.StrId()} \"{node.DisplayName}\" has a duplicate ID, prior node was \"{existingNode.DisplayName}\"");
|
||||||
};
|
}
|
||||||
|
_id2node[node.Id] = node;
|
||||||
|
}
|
||||||
|
|
||||||
_files.Add(file);
|
BuildPass1();
|
||||||
_typeNest.Push(file);
|
BuildPass2();
|
||||||
|
}
|
||||||
|
|
||||||
var fileNode = IdToNode(reqFile.Id);
|
// First pass: create type definitions for each node.
|
||||||
|
|
||||||
if (!fileNode.IsFile)
|
struct Pass1State
|
||||||
throw new InvalidSchemaException("Expected a file node");
|
{
|
||||||
|
public HashSet<ulong> unprocessedNodes;
|
||||||
|
public bool isGenerated;
|
||||||
|
public IHasNestedDefinitions parent;
|
||||||
|
}
|
||||||
|
|
||||||
ProcessFile(fileNode);
|
void BuildPass1()
|
||||||
|
{
|
||||||
_typeNest.Pop();
|
Pass1State state = new Pass1State()
|
||||||
|
{
|
||||||
|
unprocessedNodes = new HashSet<ulong>(_id2node.Keys)
|
||||||
|
};
|
||||||
|
var requestedFiles = _request.RequestedFiles.ToDictionary(req => req.Id);
|
||||||
|
foreach (var node in _id2node.Values.Where(n => n.IsFile))
|
||||||
|
{
|
||||||
|
GenFile file;
|
||||||
|
state.isGenerated = requestedFiles.TryGetValue(node.Id, out var req);
|
||||||
|
state.parent = null;
|
||||||
|
if (state.isGenerated)
|
||||||
|
{
|
||||||
|
file = (GenFile)ProcessNodePass1(node.Id, req.Filename, state);
|
||||||
|
_generatedFiles.Add(file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
file = (GenFile)ProcessNodePass1(node.Id, node.DisplayName, state);
|
||||||
|
}
|
||||||
|
_allDefinitions.Add(node.Id, file);
|
||||||
|
}
|
||||||
|
if (state.unprocessedNodes.Count != 0)
|
||||||
|
{
|
||||||
|
throw new InvalidSchemaException("Unreferenced nodes were present in the schema.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessFile(Schema.Node.Reader fileReader)
|
TypeDefinition CreateTypeDef(Schema.Node.Reader node, IHasNestedDefinitions parent)
|
||||||
{
|
{
|
||||||
foreach (var annotation in fileReader.Annotations)
|
var kind = node.GetKind();
|
||||||
|
var def = _typeDefMgr.Create(node.Id, kind.GetTypeTag());
|
||||||
|
def.DeclaringElement = parent;
|
||||||
|
if (kind == NodeKind.Group)
|
||||||
|
((TypeDefinition)parent).NestedGroups.Add(def);
|
||||||
|
else
|
||||||
|
parent.NestedTypes.Add(def);
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
IHasNestedDefinitions ProcessNodePass1(ulong id, string name, Pass1State state)
|
||||||
|
{
|
||||||
|
if (!_id2node.TryGetValue(id, out var node))
|
||||||
|
{
|
||||||
|
if (!state.isGenerated) return null;
|
||||||
|
throw new InvalidSchemaException($"The node {id.StrId()} was necessary for backend codegen but is missing.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.unprocessedNodes.Remove(id))
|
||||||
|
throw new InvalidSchemaException($"The node \"{node.DisplayName}\" {node.StrId()} has been declared recursively.");
|
||||||
|
|
||||||
|
GenFile file = null;
|
||||||
|
bool processNestedNodes = false;
|
||||||
|
bool processFields = false;
|
||||||
|
bool processInterfaceMethods = false;
|
||||||
|
|
||||||
|
switch (node.GetKind())
|
||||||
|
{
|
||||||
|
case NodeKind.Annotation:
|
||||||
|
case NodeKind.Const:
|
||||||
|
// A dummy TypeDefinition is created to node hierarchy
|
||||||
|
break;
|
||||||
|
case NodeKind.File:
|
||||||
|
if (state.parent != null)
|
||||||
|
throw new InvalidSchemaException("Did not expect file nodes to appear as nested nodes");
|
||||||
|
file = new GenFile();
|
||||||
|
file.Namespace = null; // TODO
|
||||||
|
processNestedNodes = true;
|
||||||
|
break;
|
||||||
|
case NodeKind.Enum:
|
||||||
|
break;
|
||||||
|
case NodeKind.Interface:
|
||||||
|
processNestedNodes = true;
|
||||||
|
processFields = true;
|
||||||
|
processInterfaceMethods = true;
|
||||||
|
break;
|
||||||
|
case NodeKind.Struct:
|
||||||
|
case NodeKind.Group:
|
||||||
|
processNestedNodes = true;
|
||||||
|
processFields = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InvalidSchemaException($"Don't know how to process node {node.StrId()} \"{node.DisplayName}\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeDefinition def = null;
|
||||||
|
if (file != null)
|
||||||
|
{
|
||||||
|
state.parent = file;
|
||||||
|
file.Name = name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state.parent = def = CreateTypeDef(node, state.parent);
|
||||||
|
def.Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processNestedNodes && node.NestedNodes != null)
|
||||||
|
foreach (var nested in node.NestedNodes)
|
||||||
|
{
|
||||||
|
ProcessNodePass1(nested.Id, nested.Name, state);
|
||||||
|
}
|
||||||
|
if (processFields && node.Fields != null)
|
||||||
|
foreach (var field in node.Fields.Where(f => f.IsGroup))
|
||||||
|
{
|
||||||
|
var group = IdToNode(field.Group_TypeId);
|
||||||
|
if (!group.IsStruct || !group.Struct_IsGroup)
|
||||||
|
{
|
||||||
|
throw new InvalidSchemaException($"Expected node with id {group.StrId()} to be a struct definition");
|
||||||
|
}
|
||||||
|
ProcessNodePass1(field.Group_TypeId, field.Name, state);
|
||||||
|
}
|
||||||
|
if (processInterfaceMethods && node.Interface_Methods != null)
|
||||||
|
foreach (var method in node.Interface_Methods)
|
||||||
|
{
|
||||||
|
var pnode = IdToNode(method.ParamStructType);
|
||||||
|
if (pnode.ScopeId == 0) ProcessNodePass1(pnode.Id, null, state); // Anonymous generated type
|
||||||
|
pnode = IdToNode(method.ResultStructType);
|
||||||
|
if (pnode.ScopeId == 0) ProcessNodePass1(pnode.Id, null, state); // Anonymous generated type
|
||||||
|
}
|
||||||
|
return state.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2nd pass: Generate types based on definitions
|
||||||
|
|
||||||
|
struct Pass2State
|
||||||
|
{
|
||||||
|
public Method currentMethod;
|
||||||
|
public HashSet<ulong> processedNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildPass2()
|
||||||
|
{
|
||||||
|
var files = _allDefinitions.Select(d => (Id: d.Key, File: d.Value as GenFile)).Where(d => d.File != null);
|
||||||
|
var state = new Pass2State() { processedNodes = new HashSet<ulong>() };
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
var node = IdToNode(file.Id);
|
||||||
|
ProcessFileAnnotations(node, file.File);
|
||||||
|
ProcessNestedNodes(node.NestedNodes, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessNestedNodes(IEnumerable<Schema.Node.NestedNode.Reader> nestedNodes, Pass2State state)
|
||||||
|
{
|
||||||
|
foreach (var nestedNode in nestedNodes)
|
||||||
|
{
|
||||||
|
ProcessNode(nestedNode.Id, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessFileAnnotations(Schema.Node.Reader fileNode, GenFile fileElement)
|
||||||
|
{
|
||||||
|
foreach (var annotation in fileNode.Annotations)
|
||||||
{
|
{
|
||||||
if (annotation.Id == 0xb9c6f99ebf805f2c) // Cxx namespace
|
if (annotation.Id == 0xb9c6f99ebf805f2c) // Cxx namespace
|
||||||
{
|
{
|
||||||
((GenFile)_typeNest.Peek()).Namespace = annotation.Value.Text.Split("::");
|
fileElement.Namespace = annotation.Value.Text.Split(new string[1] { "::" }, default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var nestedNode in fileReader.NestedNodes)
|
|
||||||
{
|
|
||||||
var node = IdToNode(nestedNode.Id);
|
|
||||||
|
|
||||||
ProcessNode(node, nestedNode.Name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeDefinition GetOrCreateTypeDef(ulong id, TypeTag tag) => _typeDefMgr.GetOrCreate(id, tag);
|
void ProcessBrand(Schema.Brand.Reader brandReader, Type type, Pass2State state)
|
||||||
TypeDefinition GetGroupTypeDef(ulong id, string name)
|
|
||||||
{
|
|
||||||
var nodeReader = _id2node[id];
|
|
||||||
|
|
||||||
if (!nodeReader.IsStruct)
|
|
||||||
throw new InvalidSchemaException($"Expected node with id {id} to be a struct definition");
|
|
||||||
|
|
||||||
return ProcessStruct(nodeReader, name);
|
|
||||||
}
|
|
||||||
void ProcessBrand(Schema.Brand.Reader brandReader, Type type)
|
|
||||||
{
|
{
|
||||||
foreach (var scopeReader in brandReader.Scopes)
|
foreach (var scopeReader in brandReader.Scopes)
|
||||||
{
|
{
|
||||||
var whatToBind = GetOrCreateTypeDef(scopeReader.ScopeId, TypeTag.Unknown);
|
var whatToBind = ProcessNode(scopeReader.ScopeId, state);
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
||||||
switch (0)
|
switch (0)
|
||||||
@ -121,7 +247,7 @@ namespace CapnpC.Model
|
|||||||
switch (0)
|
switch (0)
|
||||||
{
|
{
|
||||||
case 0 when bindingReader.IsType:
|
case 0 when bindingReader.IsType:
|
||||||
type.BindGenericParameter(typeParameter, ProcessType(bindingReader.Type));
|
type.BindGenericParameter(typeParameter, ProcessType(bindingReader.Type, state));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0 when bindingReader.IsUnbound:
|
case 0 when bindingReader.IsUnbound:
|
||||||
@ -146,7 +272,8 @@ namespace CapnpC.Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Type ProcessType(Schema.Type.Reader typeReader)
|
|
||||||
|
Type ProcessType(Schema.Type.Reader typeReader, Pass2State state)
|
||||||
{
|
{
|
||||||
Type result;
|
Type result;
|
||||||
|
|
||||||
@ -159,7 +286,7 @@ namespace CapnpC.Model
|
|||||||
return Types.FromParameter(
|
return Types.FromParameter(
|
||||||
new GenericParameter()
|
new GenericParameter()
|
||||||
{
|
{
|
||||||
DeclaringEntity = GetOrCreateTypeDef(typeReader.AnyPointer_Parameter_ScopeId, TypeTag.Unknown),
|
DeclaringEntity = ProcessNode(typeReader.AnyPointer_Parameter_ScopeId, state),
|
||||||
Index = typeReader.AnyPointer_Parameter_ParameterIndex
|
Index = typeReader.AnyPointer_Parameter_ParameterIndex
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -167,7 +294,7 @@ namespace CapnpC.Model
|
|||||||
return Types.FromParameter(
|
return Types.FromParameter(
|
||||||
new GenericParameter()
|
new GenericParameter()
|
||||||
{
|
{
|
||||||
DeclaringEntity = _currentMethod ?? throw new InvalidOperationException("current method not set"),
|
DeclaringEntity = state.currentMethod ?? throw new InvalidOperationException("current method not set"),
|
||||||
Index = typeReader.AnyPointer_ImplicitMethodParameter_ParameterIndex
|
Index = typeReader.AnyPointer_ImplicitMethodParameter_ParameterIndex
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -205,7 +332,7 @@ namespace CapnpC.Model
|
|||||||
return Types.F64;
|
return Types.F64;
|
||||||
|
|
||||||
case 0 when typeReader.IsEnum:
|
case 0 when typeReader.IsEnum:
|
||||||
return Types.FromDefinition(GetOrCreateTypeDef(typeReader.Enum_TypeId, TypeTag.Enum));
|
return Types.FromDefinition(ProcessNode(typeReader.Enum_TypeId, state, TypeTag.Enum));
|
||||||
|
|
||||||
case 0 when typeReader.IsFloat32:
|
case 0 when typeReader.IsFloat32:
|
||||||
return Types.F32;
|
return Types.F32;
|
||||||
@ -223,16 +350,16 @@ namespace CapnpC.Model
|
|||||||
return Types.S8;
|
return Types.S8;
|
||||||
|
|
||||||
case 0 when typeReader.IsInterface:
|
case 0 when typeReader.IsInterface:
|
||||||
result = Types.FromDefinition(GetOrCreateTypeDef(typeReader.Interface_TypeId, TypeTag.Interface));
|
result = Types.FromDefinition(ProcessNode(typeReader.Interface_TypeId, state, TypeTag.Interface));
|
||||||
ProcessBrand(typeReader.Interface_Brand, result);
|
ProcessBrand(typeReader.Interface_Brand, result, state);
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
case 0 when typeReader.IsList:
|
case 0 when typeReader.IsList:
|
||||||
return Types.List(ProcessType(typeReader.List_ElementType));
|
return Types.List(ProcessType(typeReader.List_ElementType, state));
|
||||||
|
|
||||||
case 0 when typeReader.IsStruct:
|
case 0 when typeReader.IsStruct:
|
||||||
result = Types.FromDefinition(GetOrCreateTypeDef(typeReader.Struct_TypeId, TypeTag.Struct));
|
result = Types.FromDefinition(ProcessNode(typeReader.Struct_TypeId, state, TypeTag.Struct));
|
||||||
ProcessBrand(typeReader.Struct_Brand, result);
|
ProcessBrand(typeReader.Struct_Brand, result, state);
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
case 0 when typeReader.IsText:
|
case 0 when typeReader.IsText:
|
||||||
@ -365,7 +492,7 @@ namespace CapnpC.Model
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessFields(Schema.Node.Reader reader, TypeDefinition declaringType, List<Field> fields)
|
void ProcessFields(Schema.Node.Reader reader, TypeDefinition declaringType, List<Field> fields, Pass2State state)
|
||||||
{
|
{
|
||||||
if (reader.Fields == null)
|
if (reader.Fields == null)
|
||||||
{
|
{
|
||||||
@ -389,15 +516,15 @@ namespace CapnpC.Model
|
|||||||
switch (0)
|
switch (0)
|
||||||
{
|
{
|
||||||
case 0 when fieldReader.IsGroup:
|
case 0 when fieldReader.IsGroup:
|
||||||
field.Type = Types.FromDefinition(GetGroupTypeDef(
|
var def = ProcessNode(fieldReader.Group_TypeId, state, TypeTag.Group);
|
||||||
fieldReader.Group_TypeId, fieldReader.Name));
|
field.Type = Types.FromDefinition(def);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0 when fieldReader.IsSlot:
|
case 0 when fieldReader.IsSlot:
|
||||||
field.DefaultValue = ProcessValue(fieldReader.Slot_DefaultValue);
|
field.DefaultValue = ProcessValue(fieldReader.Slot_DefaultValue);
|
||||||
field.DefaultValueIsExplicit = fieldReader.Slot_HadExplicitDefault;
|
field.DefaultValueIsExplicit = fieldReader.Slot_HadExplicitDefault;
|
||||||
field.Offset = fieldReader.Slot_Offset;
|
field.Offset = fieldReader.Slot_Offset;
|
||||||
field.Type = ProcessType(fieldReader.Slot_Type);
|
field.Type = ProcessType(fieldReader.Slot_Type, state);
|
||||||
field.DefaultValue.Type = field.Type;
|
field.DefaultValue.Type = field.Type;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -411,7 +538,7 @@ namespace CapnpC.Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessInterfaceOrStructTail(TypeDefinition def, Schema.Node.Reader reader)
|
TypeDefinition ProcessInterfaceOrStructTail(TypeDefinition def, Schema.Node.Reader reader, Pass2State state)
|
||||||
{
|
{
|
||||||
def.IsGeneric = reader.IsGeneric;
|
def.IsGeneric = reader.IsGeneric;
|
||||||
|
|
||||||
@ -423,18 +550,9 @@ namespace CapnpC.Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_typeNest.Push(def);
|
ProcessNestedNodes(reader.NestedNodes, state);
|
||||||
|
|
||||||
if (reader.NestedNodes != null)
|
ProcessFields(reader, def, def.Fields, state);
|
||||||
{
|
|
||||||
foreach (var nestedReader in reader.NestedNodes)
|
|
||||||
{
|
|
||||||
var node = IdToNode(nestedReader.Id);
|
|
||||||
ProcessNode(node, nestedReader.Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessFields(reader, def, def.Fields);
|
|
||||||
|
|
||||||
if (reader.IsInterface)
|
if (reader.IsInterface)
|
||||||
{
|
{
|
||||||
@ -450,12 +568,12 @@ namespace CapnpC.Model
|
|||||||
{
|
{
|
||||||
method.GenericParameters.Add(implicitParameterReader.Name);
|
method.GenericParameters.Add(implicitParameterReader.Name);
|
||||||
}
|
}
|
||||||
_currentMethod = method;
|
state.currentMethod = method;
|
||||||
|
|
||||||
def.Methods.Add(method);
|
def.Methods.Add(method);
|
||||||
|
|
||||||
var paramNode = IdToNode(methodReader.ParamStructType);
|
var paramNode = IdToNode(methodReader.ParamStructType);
|
||||||
var paramType = ProcessParameterList(paramNode, methodReader.ParamBrand, method.Params);
|
var paramType = ProcessParameterList(paramNode, methodReader.ParamBrand, method.Params, state);
|
||||||
if (paramType != null)
|
if (paramType != null)
|
||||||
{
|
{
|
||||||
paramType.SpecialName = SpecialName.MethodParamsStruct;
|
paramType.SpecialName = SpecialName.MethodParamsStruct;
|
||||||
@ -470,7 +588,7 @@ namespace CapnpC.Model
|
|||||||
method.ParamsStruct.InheritFreeParameters(method);
|
method.ParamsStruct.InheritFreeParameters(method);
|
||||||
|
|
||||||
var resultNode = IdToNode(methodReader.ResultStructType);
|
var resultNode = IdToNode(methodReader.ResultStructType);
|
||||||
var resultType = ProcessParameterList(resultNode, methodReader.ResultBrand, method.Results);
|
var resultType = ProcessParameterList(resultNode, methodReader.ResultBrand, method.Results, state);
|
||||||
if (resultType != null)
|
if (resultType != null)
|
||||||
{
|
{
|
||||||
resultType.SpecialName = SpecialName.MethodResultStruct;
|
resultType.SpecialName = SpecialName.MethodResultStruct;
|
||||||
@ -485,24 +603,13 @@ namespace CapnpC.Model
|
|||||||
method.ResultStruct.InheritFreeParameters(method);
|
method.ResultStruct.InheritFreeParameters(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentMethod = null;
|
state.currentMethod = null;
|
||||||
}
|
}
|
||||||
|
return def;
|
||||||
_typeNest.Pop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeDefinition ProcessStruct(Schema.Node.Reader structReader, string name)
|
TypeDefinition ProcessStruct(Schema.Node.Reader structReader, TypeDefinition def, Pass2State state)
|
||||||
{
|
{
|
||||||
var def = GetOrCreateTypeDef(
|
|
||||||
structReader.Id,
|
|
||||||
structReader.Struct_IsGroup ? TypeTag.Group : TypeTag.Struct);
|
|
||||||
|
|
||||||
def.DeclaringElement = _typeNest.Peek();
|
|
||||||
if (structReader.Struct_IsGroup)
|
|
||||||
((TypeDefinition)def.DeclaringElement).NestedGroups.Add(def);
|
|
||||||
else
|
|
||||||
def.DeclaringElement.NestedTypes.Add(def);
|
|
||||||
def.Name = name;
|
|
||||||
def.StructDataWordCount = structReader.Struct_DataWordCount;
|
def.StructDataWordCount = structReader.Struct_DataWordCount;
|
||||||
def.StructPointerCount = structReader.Struct_PointerCount;
|
def.StructPointerCount = structReader.Struct_PointerCount;
|
||||||
|
|
||||||
@ -513,12 +620,10 @@ namespace CapnpC.Model
|
|||||||
16u * structReader.Struct_DiscriminantOffset);
|
16u * structReader.Struct_DiscriminantOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessInterfaceOrStructTail(def, structReader);
|
return ProcessInterfaceOrStructTail(def, structReader, state);
|
||||||
|
|
||||||
return def;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeDefinition ProcessParameterList(Schema.Node.Reader reader, Schema.Brand.Reader brandReader, List<Field> list)
|
TypeDefinition ProcessParameterList(Schema.Node.Reader reader, Schema.Brand.Reader brandReader, List<Field> list, Pass2State state)
|
||||||
{
|
{
|
||||||
//# If a named parameter list was specified in the method
|
//# If a named parameter list was specified in the method
|
||||||
//# declaration (rather than a single struct parameter type) then a corresponding struct type is
|
//# declaration (rather than a single struct parameter type) then a corresponding struct type is
|
||||||
@ -533,58 +638,38 @@ namespace CapnpC.Model
|
|||||||
throw new InvalidSchemaException("Expected a struct");
|
throw new InvalidSchemaException("Expected a struct");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var def = ProcessNode(reader.Id, state, TypeTag.Struct);
|
||||||
|
|
||||||
if (reader.ScopeId == 0)
|
if (reader.ScopeId == 0)
|
||||||
{
|
{
|
||||||
// Auto-generated => Named parameter list
|
// Auto-generated => Named parameter list
|
||||||
ProcessFields(reader, null, list);
|
foreach (var field in def.Fields) list.Add(field);
|
||||||
return ProcessStruct(reader, null);
|
return def;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Single, anonymous, struct-typed parameter
|
// Single, anonymous, struct-typed parameter
|
||||||
var def = GetOrCreateTypeDef(reader.Id, TypeTag.Struct);
|
|
||||||
var type = Types.FromDefinition(def);
|
var type = Types.FromDefinition(def);
|
||||||
ProcessBrand(brandReader, type);
|
ProcessBrand(brandReader, type, state);
|
||||||
var anon = new Field() { Type = type };
|
var anon = new Field() { Type = type };
|
||||||
list.Add(anon);
|
list.Add(anon);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeDefinition ProcessInterface(Schema.Node.Reader ifaceReader, string name)
|
TypeDefinition ProcessInterface(Schema.Node.Reader ifaceReader, TypeDefinition def, Pass2State state)
|
||||||
{
|
{
|
||||||
var def = GetOrCreateTypeDef(
|
|
||||||
ifaceReader.Id,
|
|
||||||
TypeTag.Interface);
|
|
||||||
|
|
||||||
def.DeclaringElement = _typeNest.Peek();
|
|
||||||
def.DeclaringElement.NestedTypes.Add(def);
|
|
||||||
def.Name = name;
|
|
||||||
|
|
||||||
foreach (var superClassReader in ifaceReader.Interface_Superclasses)
|
foreach (var superClassReader in ifaceReader.Interface_Superclasses)
|
||||||
{
|
{
|
||||||
var superClass = GetOrCreateTypeDef(
|
var superClass = ProcessNode(superClassReader.Id, state, TypeTag.Interface);
|
||||||
superClassReader.Id,
|
|
||||||
TypeTag.Interface);
|
|
||||||
|
|
||||||
def.Superclasses.Add(Types.FromDefinition(superClass));
|
def.Superclasses.Add(Types.FromDefinition(superClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessInterfaceOrStructTail(def, ifaceReader);
|
return ProcessInterfaceOrStructTail(def, ifaceReader, state);
|
||||||
|
|
||||||
return def;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessEnum(Schema.Node.Reader enumReader, string name)
|
TypeDefinition ProcessEnum(Schema.Node.Reader enumReader, TypeDefinition def, Pass2State state)
|
||||||
{
|
{
|
||||||
var def = GetOrCreateTypeDef(enumReader.Id, TypeTag.Enum);
|
|
||||||
|
|
||||||
def.DeclaringElement = _typeNest.Peek();
|
|
||||||
def.DeclaringElement.NestedTypes.Add(def);
|
|
||||||
def.Name = name;
|
|
||||||
|
|
||||||
_typeNest.Push(def);
|
|
||||||
|
|
||||||
foreach (var fieldReader in enumReader.Enumerants)
|
foreach (var fieldReader in enumReader.Enumerants)
|
||||||
{
|
{
|
||||||
var field = new Enumerant()
|
var field = new Enumerant()
|
||||||
@ -601,46 +686,41 @@ namespace CapnpC.Model
|
|||||||
|
|
||||||
def.Enumerants.Add(field);
|
def.Enumerants.Add(field);
|
||||||
}
|
}
|
||||||
|
return def;
|
||||||
_typeNest.Pop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessConst(Schema.Node.Reader constReader, string name)
|
Value ProcessConst(Schema.Node.Reader constReader, Pass2State state)
|
||||||
{
|
{
|
||||||
var value = ProcessValue(constReader.Const_Value);
|
var value = ProcessValue(constReader.Const_Value);
|
||||||
value.Type = ProcessType(constReader.Const_Type);
|
value.Type = ProcessType(constReader.Const_Type, state);
|
||||||
|
return value;
|
||||||
_typeNest.Peek().Constants.Add(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessNode(Schema.Node.Reader node, string name)
|
TypeDefinition ProcessNode(ulong id, Pass2State state, TypeTag tag = default)
|
||||||
{
|
{
|
||||||
switch (0)
|
var node = IdToNode(id);
|
||||||
|
var kind = node.GetKind();
|
||||||
|
if (tag == TypeTag.Unknown) tag = kind.GetTypeTag();
|
||||||
|
var def = _typeDefMgr.GetExisting(id, tag);
|
||||||
|
if (state.processedNodes.Contains(id)) return def;
|
||||||
|
state.processedNodes.Add(id);
|
||||||
|
|
||||||
|
switch (kind)
|
||||||
{
|
{
|
||||||
case 0 when node.IsAnnotation:
|
case NodeKind.Annotation:
|
||||||
break;
|
return def;
|
||||||
|
case NodeKind.Const:
|
||||||
case 0 when node.IsConst:
|
def.DeclaringElement.Constants.Add(ProcessConst(node, state));
|
||||||
ProcessConst(node, name);
|
return def;
|
||||||
break;
|
case NodeKind.Enum:
|
||||||
|
return ProcessEnum(node, def, state);
|
||||||
case 0 when node.IsEnum:
|
case NodeKind.Interface:
|
||||||
ProcessEnum(node, name);
|
return ProcessInterface(node, def, state);
|
||||||
break;
|
case NodeKind.Struct:
|
||||||
|
case NodeKind.Group:
|
||||||
case 0 when node.IsFile:
|
return ProcessStruct(node, def, state);
|
||||||
throw new InvalidSchemaException("Did not expect file nodes to appear as nested nodes");
|
|
||||||
|
|
||||||
case 0 when node.IsInterface:
|
|
||||||
ProcessInterface(node, name);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0 when node.IsStruct:
|
|
||||||
ProcessStruct(node, name);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new InvalidSchemaException($"Don't know how to process node {node.DisplayName}");
|
throw new InvalidProgramException($"An unexpected node {node.StrId()} was found during the 2nd schema model building pass.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -651,4 +731,55 @@ namespace CapnpC.Model
|
|||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum NodeKind
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
Annotation,
|
||||||
|
Const,
|
||||||
|
Enum,
|
||||||
|
File,
|
||||||
|
Interface,
|
||||||
|
Struct,
|
||||||
|
Group
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SchemaExtensions
|
||||||
|
{
|
||||||
|
public static string GetName(this Schema.Node.Reader node)
|
||||||
|
=> node.DisplayName.Substring((int)node.DisplayNamePrefixLength);
|
||||||
|
|
||||||
|
public static string StrId(this Schema.Node.Reader node)
|
||||||
|
=> $"0x{node.Id.ToString("X")}";
|
||||||
|
|
||||||
|
public static string StrId(this ulong nodeId)
|
||||||
|
=> $"0x{nodeId.ToString("X")}";
|
||||||
|
|
||||||
|
public static NodeKind GetKind(this Schema.Node.Reader node)
|
||||||
|
{
|
||||||
|
if (node.IsStruct)
|
||||||
|
return node.Struct_IsGroup ? NodeKind.Group : NodeKind.Struct;
|
||||||
|
if (node.IsInterface) return NodeKind.Interface;
|
||||||
|
if (node.IsEnum) return NodeKind.Enum;
|
||||||
|
if (node.IsConst) return NodeKind.Const;
|
||||||
|
if (node.IsAnnotation) return NodeKind.Annotation;
|
||||||
|
if (node.IsFile) return NodeKind.File;
|
||||||
|
return NodeKind.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static TypeTag GetTypeTag(this NodeKind kind)
|
||||||
|
{
|
||||||
|
switch (kind)
|
||||||
|
{
|
||||||
|
case NodeKind.Enum: return TypeTag.Enum;
|
||||||
|
case NodeKind.Interface: return TypeTag.Interface;
|
||||||
|
case NodeKind.Struct: return TypeTag.Struct;
|
||||||
|
case NodeKind.Group: return TypeTag.Group;
|
||||||
|
default: return TypeTag.Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static TypeTag GetTypeTag(this Schema.Node.Reader node)
|
||||||
|
=> node.GetKind().GetTypeTag();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,16 @@ namespace CapnpC.Model
|
|||||||
{
|
{
|
||||||
class Type: AbstractType
|
class Type: AbstractType
|
||||||
{
|
{
|
||||||
|
// Representation of a type expression in the schema language
|
||||||
|
|
||||||
public TypeDefinition Definition { get; set; }
|
public TypeDefinition Definition { get; set; }
|
||||||
|
// The model for all nodes that are not file nodes - they define types
|
||||||
|
|
||||||
public GenericParameter Parameter { get; set; }
|
public GenericParameter Parameter { get; set; }
|
||||||
|
// A reference to type parameter in this scope
|
||||||
|
|
||||||
public Type ElementType { get; set; }
|
public Type ElementType { get; set; }
|
||||||
|
// The type of a list element, if this is a list.
|
||||||
|
|
||||||
readonly Dictionary<GenericParameter, Type> _parameterBindings =
|
readonly Dictionary<GenericParameter, Type> _parameterBindings =
|
||||||
new Dictionary<GenericParameter, Type>();
|
new Dictionary<GenericParameter, Type>();
|
||||||
|
@ -8,7 +8,19 @@ namespace CapnpC.Model
|
|||||||
readonly Dictionary<ulong, TypeDefinition> _id2def =
|
readonly Dictionary<ulong, TypeDefinition> _id2def =
|
||||||
new Dictionary<ulong, TypeDefinition>();
|
new Dictionary<ulong, TypeDefinition>();
|
||||||
|
|
||||||
public TypeDefinition GetOrCreate(ulong id, TypeTag tag)
|
public TypeDefinition Create(ulong id, TypeTag tag)
|
||||||
|
{
|
||||||
|
if (_id2def.ContainsKey(id))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(nameof(id), $"Attempting to redefine {tag.ToString()} {id.StrId()}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var def = new TypeDefinition(tag, id);
|
||||||
|
_id2def.Add(id, def);
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeDefinition GetExisting(ulong id, TypeTag tag)
|
||||||
{
|
{
|
||||||
if (_id2def.TryGetValue(id, out var def))
|
if (_id2def.TryGetValue(id, out var def))
|
||||||
{
|
{
|
||||||
@ -20,19 +32,9 @@ namespace CapnpC.Model
|
|||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(tag), "Type tag does not match existing type");
|
throw new ArgumentOutOfRangeException(nameof(tag), "Type tag does not match existing type");
|
||||||
}
|
}
|
||||||
|
return def;
|
||||||
}
|
}
|
||||||
else
|
throw new ArgumentOutOfRangeException($"Attempting to retrieve nonexistend node {id.StrId()}.");
|
||||||
{
|
|
||||||
def = new TypeDefinition(tag, id);
|
|
||||||
_id2def.Add(id, def);
|
|
||||||
}
|
|
||||||
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TypeDefinition GetExisting(ulong id)
|
|
||||||
{
|
|
||||||
return _id2def[id];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user