-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathAstNode.cs
275 lines (236 loc) · 9.24 KB
/
AstNode.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
using Meadow.CoverageReport.AstTypes;
using Meadow.CoverageReport.AstTypes.Enums;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
namespace Meadow.CoverageReport
{
public class AstNode : IEquatable<AstNode>
{
#region Fields
private static Dictionary<string, AstNodeType> _nodeTypeLookup;
#endregion
#region Properties
public SourceRange SourceRange { get; }
public JObject Node { get; }
public AstNodeType NodeType { get; }
public long Id { get; }
public int SourceIndex => SourceRange.SourceIndex;
public string FileName { get; }
public string FilePath { get; }
#endregion
string _nodeJsonString;
public string NodeJsonString => _nodeJsonString ?? (_nodeJsonString = Node.ToString(Formatting.Indented));
AstNode _parent;
public AstNode Parent => _parent ?? (_parent = GetParent(this));
#region Constructor
public AstNode(JObject node)
{
SourceRange = new SourceRange(node.Value<string>("src"));
Node = node;
NodeType = GetTypeFromString(node.Value<string>("nodeType"));
FileName = node.Value<string>("file");
FilePath = node.Value<string>("absolutePath");
Id = node.Value<long>("id");
}
#endregion
#region Functions
public static T Create<T>(JObject node) where T : AstNode
{
return Create(node) as T;
}
public static AstNode Create(JObject node)
{
// If the node is null, return null.
if (node == null)
{
return null;
}
// Obtain the node type
string nodeTypeString = node.SelectToken("nodeType")?.Value<string>();
// Get our type from our string.
AstNodeType nodeType = GetTypeFromString(nodeTypeString);
// Determine what type of AST node this should be.
switch (nodeType)
{
case AstNodeType.ContractDefinition:
return new AstContractDefinition(node);
case AstNodeType.VariableDeclaration:
return new AstVariableDeclaration(node);
case AstNodeType.FunctionDefinition:
return new AstFunctionDefinition(node);
case AstNodeType.StructDefinition:
return new AstStructDefinition(node);
case AstNodeType.EnumDefinition:
return new AstEnumDefinition(node);
case AstNodeType.EnumValue:
return new AstEnumMember(node);
case AstNodeType.ElementaryTypeName:
return new AstElementaryTypeName(node);
case AstNodeType.UserDefinedTypeName:
return new AstUserDefinedTypeName(node);
case AstNodeType.ArrayTypeName:
return new AstArrayTypeName(node);
case AstNodeType.Mapping:
return new AstMappingTypeName(node);
}
// For any other type, we return a generic ast node.
return new AstNode(node);
}
public static AstNodeType GetTypeFromString(string nodeTypeString)
{
// If the node type string is null, we return none
if (string.IsNullOrEmpty(nodeTypeString))
{
return AstNodeType.None;
}
// If our type lookup isn't null
if (_nodeTypeLookup == null)
{
// Create our node lookup.
_nodeTypeLookup = new Dictionary<string, AstNodeType>(StringComparer.InvariantCultureIgnoreCase);
// Obtain every enum option for this enum
AstNodeType[] nodeTypes = (AstNodeType[])Enum.GetValues(typeof(AstNodeType));
// Loop for each node type
foreach (AstNodeType nodeType in nodeTypes)
{
// Obtain the ast node type string attribute
FieldInfo fi = nodeType.GetType().GetField(nodeType.ToString());
if (fi == null)
{
continue;
}
// Obtain all attributes of type we are interested in.
var attributes = fi.GetCustomAttributes<AstNodeTypeStringAttribute>(false).ToArray();
// If one exists, cache it and return it.
if (attributes != null && attributes.Length > 0)
{
var attribute = attributes[0];
foreach (string typeString in attribute.NodeTypeStrings)
{
_nodeTypeLookup[typeString] = nodeType;
}
}
}
}
// Try to obtain our type from lookup, if we fail return the 'other' type.
bool success = _nodeTypeLookup.TryGetValue(nodeTypeString, out AstNodeType value);
if (success)
{
return value;
}
else
{
return AstNodeType.Other;
}
}
public static AstDeclarationVisibility GetVisibilityFromString(string visibilityTypeString)
{
switch (visibilityTypeString)
{
case "private":
return AstDeclarationVisibility.Private;
case "public":
return AstDeclarationVisibility.Public;
case "external":
return AstDeclarationVisibility.External;
case "internal":
return AstDeclarationVisibility.Internal;
default:
return AstDeclarationVisibility.Public;
}
}
public static AstNode GetParent(AstNode node)
{
var parent = node.Node.Parent;
while (parent != null)
{
if (parent is JObject jobj)
{
if (jobj.ContainsKey("src"))
{
return Create(jobj);
}
}
parent = parent.Parent;
}
return null;
}
public bool ReferencesDeclaration(AstNode declarationNode, bool recursive = true)
{
// Grab the id for the declaration
long? id = declarationNode.Node.SelectToken("id")?.Value<long>();
// If our id does not exist, we could not have referenced it
if (!id.HasValue)
{
return false;
}
// Check if this references our declaration
return ReferencesDeclaration(this.Node, id.Value, recursive);
}
private static bool ReferencesDeclaration(JToken jToken, long declarationId, bool recursive = true)
{
// If this object directly references the declaration, we return true.
long? referencesDeclaration = jToken.SelectToken("referencedDeclaration")?.Value<long?>();
// If we have a value and it matches our ID, return true.
if (referencesDeclaration.HasValue && referencesDeclaration.Value == declarationId)
{
return true;
}
// If we want to check recursively
if (recursive)
{
// This object had no reference to the declaration, check our children.
var children = jToken.Children();
foreach (var child in children)
{
// Check if our child references the declaration.
if (ReferencesDeclaration(child, declarationId, recursive))
{
return true;
}
}
}
// Return false for we could not find a reference to this declaration identifier.
return false;
}
public T GetImmediateOrAncestor<T>() where T : AstNode
{
// Loop upwards trying to find an ancestor of the specified type.
AstNode currentNode = this;
while (currentNode != null)
{
// If this node is the correct type.
if (currentNode is T)
{
return (T)currentNode;
}
// Set our current node as our parent.
currentNode = currentNode.Parent;
}
// Return null if we couldn't find a node of our type
return null;
}
public override bool Equals(object obj)
{
return obj is AstNode n && Equals(n);
}
public bool Equals(AstNode other)
{
return other.SourceRange.Equals(SourceRange);
}
public override int GetHashCode()
{
return SourceRange.GetHashCode();
}
public override string ToString()
{
return $"{NodeType}, {SourceRange}";
}
#endregion
}
}