This repository has been archived by the owner on Apr 20, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
/
GlobalMapping.cs
393 lines (345 loc) · 16.2 KB
/
GlobalMapping.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
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
//
// Keep track of the slot names allocated for every field, event and method of every type definition, every interface
// method of every interface type definition, every type of every assembly
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.LiveLabs.Extras;
using CST = Microsoft.LiveLabs.CST;
using JST = Microsoft.LiveLabs.JavaScript.JST;
namespace Microsoft.LiveLabs.JavaScript.IL2JS
{
//
// Manage assembly-level and type-level slots
//
public class GlobalMapping
{
[NotNull]
private readonly CompilerEnvironment env;
[NotNull]
private readonly Map<CST.AssemblyName, AssemblyMapping> assemblyMappingCache;
[NotNull]
private readonly Map<CST.QualifiedTypeName, TypeMapping> typeMappingCache;
public GlobalMapping(CompilerEnvironment env)
{
this.env = env;
assemblyMappingCache = new Map<CST.AssemblyName, AssemblyMapping>();
typeMappingCache = new Map<CST.QualifiedTypeName, TypeMapping>();
}
private AssemblyMapping AssemblyMappingFor(CST.AssemblyDef assemblyDef)
{
var name = assemblyDef.Name;
var assemblyMapping = default(AssemblyMapping);
if (!assemblyMappingCache.TryGetValue(name, out assemblyMapping))
{
assemblyMapping = new AssemblyMapping(env, assemblyDef);
assemblyMappingCache.Add(name, assemblyMapping);
}
return assemblyMapping;
}
private TypeMapping TypeMappingFor(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef)
{
var name = typeDef.QualifiedTypeName(env.Global, assemblyDef);
var typeMapping = default(TypeMapping);
if (!typeMappingCache.TryGetValue(name, out typeMapping))
{
typeMapping = new TypeMapping(env, assemblyDef, typeDef);
typeMappingCache.Add(name, typeMapping);
}
return typeMapping;
}
public string ResolveTypeDefToSlot(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef)
{
return AssemblyMappingFor(assemblyDef).ResolveTypeDefinitionToSlot(typeDef.EffectiveName(env.Global));
}
public string ResolveTypeRefToSlot(CST.TypeRef typeRef)
{
var assemblyDef = default(CST.AssemblyDef);
if (typeRef.QualifiedTypeName.Assembly.PrimTryResolve(env.Global, out assemblyDef))
return AssemblyMappingFor(assemblyDef).ResolveTypeDefinitionToSlot(typeRef.QualifiedTypeName.Type);
else
throw new InvalidOperationException("invalid type ref");
}
public string ResolveAssemblyReferenceToSlot(CST.AssemblyDef assemblyDef, CST.AssemblyName assemblyName)
{
return AssemblyMappingFor(assemblyDef).ResolveAssemblyReferenceToSlot(assemblyName);
}
public string ResolveFieldDefToSlot(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.FieldDef fieldDef)
{
return TypeMappingFor(assemblyDef, typeDef).ResolveFieldToSlot
(fieldDef.QualifiedMemberName(env.Global, assemblyDef, typeDef));
}
public string ResolveFieldRefToSlot(CST.FieldRef fieldRef)
{
var assemblyDef = default(CST.AssemblyDef);
var typeDef = default(CST.TypeDef);
if (fieldRef.DefiningType.PrimTryResolve(env.Global, out assemblyDef, out typeDef))
return TypeMappingFor(assemblyDef, typeDef).ResolveFieldToSlot(fieldRef.QualifiedMemberName);
else
throw new InvalidOperationException("invalid field ref");
}
public string ResolveEventDefToSlot(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.EventDef eventDef)
{
return TypeMappingFor(assemblyDef, typeDef).ResolveEventToSlot
(eventDef.QualifiedMemberName(env.Global, assemblyDef, typeDef));
}
public string ResolveEventRefToSlot(CST.EventRef eventRef)
{
var assemblyDef = default(CST.AssemblyDef);
var typeDef = default(CST.TypeDef);
if (eventRef.DefiningType.PrimTryResolve(env.Global, out assemblyDef, out typeDef))
return TypeMappingFor(assemblyDef, typeDef).ResolveEventToSlot(eventRef.QualifiedMemberName);
else
throw new InvalidOperationException("invalid event ref");
}
public string ResolveMethodDefToSlot(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.MethodDef methodDef)
{
return TypeMappingFor(assemblyDef, typeDef).ResolveMethodToSlot
(methodDef.QualifiedMemberName(env.Global, assemblyDef, typeDef));
}
public string ResolveMethodRefToSlot(CST.PolymorphicMethodRef methodRef)
{
var assemblyDef = default(CST.AssemblyDef);
var typeDef = default(CST.TypeDef);
if (methodRef.DefiningType.PrimTryResolve(env.Global, out assemblyDef, out typeDef))
return TypeMappingFor(assemblyDef, typeDef).ResolveMethodToSlot(methodRef.QualifiedMemberName);
else
throw new InvalidOperationException("invalid method ref");
}
public string ResolvePropertyDefToSlot(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, CST.PropertyDef propDef)
{
return TypeMappingFor(assemblyDef, typeDef).ResolvePropertyToSlot
(propDef.QualifiedMemberName(env.Global, assemblyDef, typeDef));
}
public string ResolveStringToSlot(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef, string str)
{
return TypeMappingFor(assemblyDef, typeDef).ResolveStringToSlot(str);
}
public IEnumerable<KeyValuePair<string, string>> AllStringSlots(CST.AssemblyDef assemblyDef, CST.TypeDef typeDef)
{
return TypeMappingFor(assemblyDef, typeDef).AllStringSlots();
}
}
//
// Slots unique within an assembly:
// - type definitions
// - references to other assemblies
//
public class AssemblyMapping
{
[NotNull]
private readonly MessageContext ctxt;
// INVARIANT: Type ref is to type definition (ie no type arguments) and in this assembly
[NotNull]
private readonly SlotAllocation<CST.TypeName> types;
[NotNull]
private readonly SlotAllocation<CST.AssemblyName> referencedAssemblies;
private static void FriendlyTypeName(StringBuilder sb, CST.TypeName name)
{
var types = name.Types;
JST.Lexemes.AppendStringToFriendlyIdentifier(sb, types[types.Count - 1].ToLowerInvariant(), 15);
}
private static void FriendlyAssemblyName(StringBuilder sb, CST.AssemblyName name)
{
JST.Lexemes.AppendStringToFriendlyIdentifier(sb, name.Name, 15);
}
public AssemblyMapping(CompilerEnvironment env, CST.AssemblyDef assemblyDef)
{
ctxt = CST.MessageContextBuilders.Assembly(env.Global, assemblyDef);
// Type slots appear as field names in assembly structure, possibly without prefixes, and
// as directory names on disk
types = new SlotAllocation<CST.TypeName>(env.DebugMode, NameFlavor.LowercaseIdentifier, FriendlyTypeName);
if (assemblyDef.Name.Equals(env.Global.MsCorLibName))
{
types.Add(env.Global.ArrayTypeConstructorRef.QualifiedTypeName.Type);
types.Add(env.Global.ManagedPointerTypeConstructorRef.QualifiedTypeName.Type);
}
// Types are already in canonical order
foreach (var typeDef in assemblyDef.Types.Where(t => t.IsUsed && t.Invalid == null))
types.Add(typeDef.EffectiveName(env.Global));
// Assembly slots appear as field names in assembly structures, prefixed by 'A'
referencedAssemblies = new SlotAllocation<CST.AssemblyName>
(env.DebugMode, NameFlavor.Identifier, FriendlyAssemblyName);
var assmEnv = env.Global.Environment().AddAssembly(assemblyDef);
foreach (var nm in assmEnv.AllAssembliesInLoadOrder())
{
if (!nm.Equals(env.Global.MsCorLibName) && !nm.Equals(assemblyDef.Name))
referencedAssemblies.Add(nm);
// else: mscorlib is bound into root structure, don't need self ref
}
}
public string ResolveTypeDefinitionToSlot(CST.TypeName name)
{
return types.For(ctxt, name);
}
public string ResolveAssemblyReferenceToSlot(CST.AssemblyName assemblyName)
{
return referencedAssemblies.For(ctxt, assemblyName);
}
}
//
// Slots unique within a type and its supertypes:
// - Method definitions
// - Fields
// - Events
// - Shared strings
//
// Virtual methods derive their slot from that of the method definition which introduced the virtual slot.
// Interface methods derive their slot from that of the method in the interface type.
//
public class TypeMapping
{
[NotNull]
private readonly MessageContext ctxt;
[NotNull]
private readonly CompilerEnvironment env;
[NotNull]
private readonly CST.AssemblyDef assemblyDef;
[NotNull]
private readonly CST.TypeDef typeDef;
[NotNull]
private readonly SlotAllocation<CST.QualifiedMemberName> methodSlots;
[NotNull]
private readonly SlotAllocation<CST.QualifiedMemberName> fieldSlots;
[NotNull]
private readonly SlotAllocation<CST.QualifiedMemberName> eventSlots;
[NotNull]
private readonly SlotAllocation<CST.QualifiedMemberName> propSlots;
[CanBeNull] // defer creation until ask for string slot
private SlotAllocation<string> stringSlots;
private static void AddNames(
CompilerEnvironment env,
CST.AssemblyDef assemblyDef,
CST.TypeDef typeDef,
SlotAllocation<CST.QualifiedMemberName> methodSlots,
SlotAllocation<CST.QualifiedMemberName> fieldSlots,
SlotAllocation<CST.QualifiedMemberName> eventSlots,
SlotAllocation<CST.QualifiedMemberName> propSlots)
{
// Allocate slots for any base type so that this type's slots won't collide with them.
// NOTE: Not strictly necessary for methods, since only virtual methods of supertype may find their
// way into derived type, but seems easiest to just allocate them all.
// NOTE: Interface method slots need only be unique within their interface type since the type
// id is included in the final slot name.
if (typeDef.Extends != null)
{
var extAssemblyDef = default(CST.AssemblyDef);
var extTypeDef = default(CST.TypeDef);
if (typeDef.Extends.PrimTryResolve(env.Global, out extAssemblyDef, out extTypeDef))
AddNames(env, extAssemblyDef, extTypeDef, methodSlots, fieldSlots, eventSlots, propSlots);
}
// Members are already in canonical order
foreach (var memberDef in typeDef.Members.Where(m => m.IsUsed && m.Invalid == null))
{
var name = memberDef.QualifiedMemberName(env.Global, assemblyDef, typeDef);
switch (memberDef.Flavor)
{
case CST.MemberDefFlavor.Field:
{
fieldSlots.Add(name);
break;
}
case CST.MemberDefFlavor.Event:
{
eventSlots.Add(name);
break;
}
case CST.MemberDefFlavor.Method:
{
methodSlots.Add(name);
break;
}
case CST.MemberDefFlavor.Property:
{
propSlots.Add(name);
break;
}
default:
throw new ArgumentOutOfRangeException();
}
}
return;
}
private static void FriendlyMemberName(StringBuilder sb, CST.QualifiedMemberName name)
{
var types = name.DefiningType.Type.Types;
JST.Lexemes.AppendStringToFriendlyIdentifier(sb, types[types.Count - 1], 15);
sb.Append('_');
var i = name.Signature.Name.LastIndexOf('.');
var nm = i >= 0 && i < name.Signature.Name.Length - 1
? name.Signature.Name.Substring(i + 1)
: name.Signature.Name;
JST.Lexemes.AppendStringToFriendlyIdentifier(sb, nm, 15);
}
private static void FriendlyStringName(StringBuilder sb, string str)
{
JST.Lexemes.AppendStringToFriendlyIdentifier(sb, str, 15);
}
public TypeMapping(CompilerEnvironment env, CST.AssemblyDef assemblyDef, CST.TypeDef typeDef)
{
ctxt = CST.MessageContextBuilders.Type(env.Global, assemblyDef, typeDef);
this.env = env;
this.assemblyDef = assemblyDef;
this.typeDef = typeDef;
// Method slots appear as field names in type structures and directory names on disk.
// Thus we use lower-case identifiers.
methodSlots = new SlotAllocation<CST.QualifiedMemberName>
(env.DebugMode, NameFlavor.LowercaseIdentifier, FriendlyMemberName);
// Field slots appear in object annd type structuers, but always prefixed by 'S' or 'F'.
// Thus we use arbitrary identifiers.
fieldSlots = new SlotAllocation<CST.QualifiedMemberName>
(env.DebugMode, NameFlavor.Identifier, FriendlyMemberName);
// Similarly for event slots, but prefixed by 'E'.
eventSlots = new SlotAllocation<CST.QualifiedMemberName>
(env.DebugMode, NameFlavor.Identifier, FriendlyMemberName);
// Similarly for property slots (needed only by reflection), but prefixed by 'R'
propSlots = new SlotAllocation<CST.QualifiedMemberName>
(env.DebugMode, NameFlavor.Identifier, FriendlyMemberName);
AddNames(env, assemblyDef, typeDef, methodSlots, fieldSlots, eventSlots, propSlots);
// Defer till ask for string slot
stringSlots = null;
}
public string ResolveMethodToSlot(CST.QualifiedMemberName name)
{
return methodSlots.For(ctxt, name);
}
public string ResolveFieldToSlot(CST.QualifiedMemberName name)
{
return fieldSlots.For(ctxt, name);
}
public string ResolveEventToSlot(CST.QualifiedMemberName name)
{
return eventSlots.For(ctxt, name);
}
public string ResolvePropertyToSlot(CST.QualifiedMemberName name)
{
return propSlots.For(ctxt, name);
}
public string ResolveStringToSlot(string str)
{
if (stringSlots == null)
{
// String slots are only used as object fields
stringSlots = new SlotAllocation<string>(env.DebugMode, NameFlavor.Identifier, FriendlyStringName);
// Collect statistincs on string literals
var stringStats = new StringStats();
stringStats.Collect(env.Global, assemblyDef, typeDef);
// Setup slots for shared strings
foreach (var kv in stringStats)
{
if (kv.Value == StringBindScope.Type)
stringSlots.Add(kv.Key);
}
}
return stringSlots.HasSlot(str) ? stringSlots.For(ctxt, str) : null;
}
public IEnumerable<KeyValuePair<string, string>> AllStringSlots()
{
if (stringSlots == null)
stringSlots = new SlotAllocation<string>(env.DebugMode, NameFlavor.Identifier, FriendlyStringName);
return stringSlots;
}
}
}