[Tizen] Add mcj-edit.py tool that can modify MultiCoreJit profiles.
[platform/upstream/dotnet/runtime.git] / src / coreclr / tools / mcj-edit / mcj-edit.py
1 #
2 # The MIT License (MIT)
3 #
4 # Copyright (c) 2022 Samsung Electronics Co., Ltd.
5 #
6 # All rights reserved.
7 #
8 # Permission is hereby granted, free of charge, to any person obtaining a copy
9 # of this software and associated documentation files (the "Software"), to deal
10 # in the Software without restriction, including without limitation the rights
11 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 # copies of the Software, and to permit persons to whom the Software is
13 # furnished to do so, subject to the following conditions:
14 #
15 # The above copyright notice and this permission notice shall be included in all
16 # copies or substantial portions of the Software.
17 #
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 # SOFTWARE.
25 #
26
27 from abc import ABC, abstractmethod
28 import argparse
29 import subprocess
30
31 # ----------------------------------------------------------------------------------------------------------------------
32 # Base class for error exceptions
33 class Error(Exception):
34     pass
35
36 # Error with message
37 class MessageError(Error):
38     def __init__(self, message):
39         super().__init__()
40         self.message = message
41
42 # Error with message about mcj profile inconsistency
43 class ProfileInconsistencyError(MessageError):
44     def __init__(self):
45         super().__init__("Inconsistency in mcj format")
46
47 # Error with message about mcj-edit internal error
48 class InternalError(MessageError):
49     def __init__(self):
50         super().__init__("Internal error in mcj-edit")
51
52 # Error with message about unsupported case in mcj-edit
53 class UnsupportedError(MessageError):
54     def __init__(self):
55         super().__init__("Unsupported case in mcj-edit")
56
57 # ----------------------------------------------------------------------------------------------------------------------
58 # Class with helper functions
59 class Utility:
60
61     # Align value to specified alignment
62     @staticmethod
63     def alignUp(value, alignment):
64         if (value is None or alignment is None):
65             raise InternalError()
66         if (not issubclass(type(value), int) or not issubclass(type(alignment), int)):
67             raise InternalError()
68         if (value < 0 or alignment < 0):
69             raise InternalError()
70         return (value + alignment - 1) // alignment * alignment
71
72     # Merge bytes into a value
73     @staticmethod
74     def mergeNBytes(bytesArr, isLittleEndian=True):
75         if (bytesArr is None or isLittleEndian is None):
76             raise InternalError()
77         if (not issubclass(type(bytesArr), list) or not issubclass(type(isLittleEndian), bool)):
78             raise InternalError()
79
80         value = 0
81
82         for index, byte in enumerate(bytesArr):
83             if (not issubclass(type(byte), int)):
84                 raise InternalError()
85             if (byte < 0 or byte > Utility.getMaxForNBytes(1)):
86                 raise InternalError()
87
88             if (isLittleEndian):
89                 value = value | (byte << index * 8)
90             else:
91                 value = (value << 8) | byte
92
93         return value
94
95     # Split bytes of value
96     @staticmethod
97     def splitNBytes(value, numbytes, isLittleEndian=True):
98         if (value is None or numbytes is None or isLittleEndian is None):
99             raise InternalError()
100         if (not issubclass(type(value), int) or not issubclass(type(numbytes), int)
101             or not issubclass(type(isLittleEndian), bool)):
102             raise InternalError()
103         if (value < 0 or numbytes < 0):
104             raise InternalError()
105
106         bytesArr = []
107
108         for index in range(numbytes): # pylint: disable=unused-variable
109             if (isLittleEndian):
110                 bytesArr.append(value & 0xff)
111             else:
112                 bytesArr.insert(0, value & 0xff)
113             value = value >> 8
114
115         if (value > 0):
116             raise InternalError()
117
118         return bytesArr
119
120     # Pack bits: 8 high bits for value1, 24 low bits for value2
121     @staticmethod
122     def pack8_24(value1, value2): # pylint: disable=invalid-name
123         if (value1 is None or value2 is None):
124             raise InternalError()
125         if (not issubclass(type(value1), int) or not issubclass(type(value2), int)):
126             raise InternalError()
127         if (value1 < 0 or value1 > Utility.getMaxForNBytes(1)
128             or value2 < 0 or value2 > Utility.getMaxForNBytes(3)):
129             raise InternalError()
130         return (value1 << 24) | value2
131
132     # Get max allowed value for unsigned N bits
133     @staticmethod
134     def getMaxForNBits(numBits):
135         if (numBits is None):
136             raise InternalError()
137         if (not issubclass(type(numBits), int)):
138             raise InternalError()
139         if (numBits < 0):
140             raise InternalError()
141         return (1 << numBits) - 1
142
143     # Get max allowed value for unsigned N bytes
144     @staticmethod
145     def getMaxForNBytes(numBytes):
146         if (numBytes is None):
147             raise InternalError()
148         if (not issubclass(type(numBytes), int)):
149             raise InternalError()
150         if (numBytes < 0):
151             raise InternalError()
152         return Utility.getMaxForNBits(numBytes * 8)
153
154 # ----------------------------------------------------------------------------------------------------------------------
155 # These are some constants from runtime, which should be checked before usage!
156 # See:
157 # - src/coreclr/vm/multicorejitimpl.h
158 # - ...
159 #
160 # TODO: add function that checks/sets these by parsing file from runtime
161 class RuntimeConstants: # pylint: disable=too-few-public-methods
162     METHOD_FLAGS_MASK = 0xff0000
163     JIT_BY_APP_THREAD_TAG = 0x10000
164     RECORD_TYPE_OFFSET = 24
165     MAX_MODULES = 0x1000
166     MODULE_MASK = 0xffff
167     MODULE_LEVEL_OFFSET = 16
168     MAX_MODULE_LEVELS = 0x100
169     MAX_METHODS = 0x4000
170     #SIGNATURE_LENGTH_MASK = 0xffff
171     HEADER_W_COUNTER = 14
172     HEADER_D_COUNTER = 3
173     #MULTICOREJITLIFE = 60 * 1000
174     #MAX_WALKBACK = 128
175
176     MULTICOREJIT_PROFILE_VERSION = 102
177     MULTICOREJIT_HEADER_RECORD_ID = 1
178     MULTICOREJIT_MODULE_RECORD_ID = 2
179     MULTICOREJIT_MODULEDEPENDENCY_RECORD_ID = 3
180     MULTICOREJIT_METHOD_RECORD_ID = 4
181     MULTICOREJIT_GENERICMETHOD_RECORD_ID = 5
182
183     # Next constant are used directly and have no named variables in runtime
184     X_MODULE_RECORD_LEN_MASK = 0xffffff
185
186 # ----------------------------------------------------------------------------------------------------------------------
187 # These are sizes of types in bytes
188 class RuntimeTypeSizes: # pylint: disable=too-few-public-methods
189     int = 4
190     short = 2
191     char = 1
192
193 # ----------------------------------------------------------------------------------------------------------------------
194 # Header of MCJ profile.
195 #
196 # Corresponds to "struct HeaderRecord" from multicorejitimpl.h
197 #
198 # To create from bytes: HeaderRecord.createFromBytes(bytes)
199 # To create from data structures using references: HeaderRecord.createByRef(...)
200 # To create from data structures using copy: HeaderRecord.createByCopy(...)
201 #
202 class HeaderRecord: # pylint: disable=too-many-instance-attributes
203     Alignment = RuntimeTypeSizes.int
204
205     Size = 6 * RuntimeTypeSizes.int \
206            + Utility.alignUp(RuntimeConstants.HEADER_W_COUNTER * RuntimeTypeSizes.short, Alignment) \
207            + RuntimeConstants.HEADER_D_COUNTER * RuntimeTypeSizes.int
208
209     # Empty constructor, do not use it directly
210     # Create new objects by createByCopy or createByRef
211     def __init__(self):
212         self._recordId = None
213         self._version = None
214         self._timeStamp = None
215         self._moduleCount = None
216         self._methodCount = None
217         self._moduleDepCount = None
218         self._shortCounters = None
219         self._longCounters = None
220
221     # Equality comparison operator
222     def __eq__(self, rhs):
223         if (rhs is None):
224             return False
225         if (not issubclass(type(rhs), HeaderRecord)):
226             return False
227         return self._recordId == rhs._recordId and self._version == rhs._version \
228                and self._timeStamp == rhs._timeStamp and self._moduleCount == rhs._moduleCount \
229                and self._methodCount == rhs._methodCount and self._moduleDepCount == rhs._moduleDepCount \
230                and self._shortCounters == rhs._shortCounters and self._longCounters == rhs._longCounters
231
232     # Get moduleCount
233     def getModuleCount(self):
234         return self._moduleCount
235
236     # Get methodCount
237     def getMethodCount(self):
238         return self._methodCount
239
240     # Get moduleDepCount
241     def getModuleDepCount(self):
242         return self._moduleDepCount
243
244     # Set moduleCount
245     def setModuleCount(self, moduleCount):
246         self._moduleCount = moduleCount
247         self.verify()
248
249     # Set methodCount
250     def setMethodCount(self, methodCount):
251         self._methodCount = methodCount
252         self.verify()
253
254     # Set moduleDepCount
255     def setModuleDepCount(self, moduleDepCount):
256         self._moduleDepCount = moduleDepCount
257         self.verify()
258
259     # 0-init usage stats
260     def dropGlobalUsageStats(self):
261         self._shortCounters = [0] * RuntimeConstants.HEADER_W_COUNTER
262         self._longCounters = [0] * RuntimeConstants.HEADER_D_COUNTER
263         self.verify()
264
265     # Verify consistency
266     def verify(self):
267         if (self._recordId is None or self._version is None
268             or self._timeStamp is None or self._moduleCount is None
269             or self._methodCount is None or self._moduleDepCount is None
270             or self._shortCounters is None or self._longCounters is None):
271             raise InternalError()
272
273         if (not issubclass(type(self._recordId), int) or not issubclass(type(self._version), int)
274             or not issubclass(type(self._timeStamp), int) or not issubclass(type(self._moduleCount), int)
275             or not issubclass(type(self._methodCount), int) or not issubclass(type(self._moduleDepCount), int)
276             or not issubclass(type(self._shortCounters), list) or not issubclass(type(self._longCounters), list)):
277             raise InternalError()
278
279         if (self._recordId < 0 or self._recordId > Utility.getMaxForNBytes(RuntimeTypeSizes.int)
280             or self._version < 0 or self._version > Utility.getMaxForNBytes(RuntimeTypeSizes.int)
281             or self._timeStamp < 0 or self._timeStamp > Utility.getMaxForNBytes(RuntimeTypeSizes.int)
282             or self._moduleCount < 0 or self._moduleCount > Utility.getMaxForNBytes(RuntimeTypeSizes.int)
283             or self._methodCount < 0 or self._methodCount > Utility.getMaxForNBytes(RuntimeTypeSizes.int)
284             or self._moduleDepCount < 0 or self._moduleDepCount > Utility.getMaxForNBytes(RuntimeTypeSizes.int)
285             or len(self._shortCounters) != RuntimeConstants.HEADER_W_COUNTER
286             or len(self._longCounters) != RuntimeConstants.HEADER_D_COUNTER):
287             raise InternalError()
288
289         for i in self._shortCounters:
290             if (i < 0 or i > Utility.getMaxForNBytes(RuntimeTypeSizes.short)):
291                 raise InternalError()
292
293         for i in self._longCounters:
294             if (i < 0 or i > Utility.getMaxForNBytes(RuntimeTypeSizes.short)):
295                 raise InternalError()
296
297         if (self._version != RuntimeConstants.MULTICOREJIT_PROFILE_VERSION):
298             raise ProfileInconsistencyError()
299
300         if (self._moduleCount > RuntimeConstants.MAX_MODULES):
301             raise ProfileInconsistencyError()
302
303         if (self._methodCount > RuntimeConstants.MAX_METHODS):
304             raise ProfileInconsistencyError()
305
306         if (self._recordId != Utility.pack8_24(RuntimeConstants.MULTICOREJIT_HEADER_RECORD_ID, HeaderRecord.Size)):
307             raise ProfileInconsistencyError()
308
309     # Print raw
310     def printRaw(self, offsetStr=""):
311         if (offsetStr is None or not issubclass(type(offsetStr), str)):
312             raise InternalError()
313
314         print(offsetStr + ">>> Raw HeaderRecord:")
315         print(offsetStr + "recordId = " + str(self._recordId))
316         print(offsetStr + "version = " + str(self._version))
317         print(offsetStr + "timeStamp = " + str(self._timeStamp))
318         print(offsetStr + "moduleCount = " + str(self._moduleCount))
319         print(offsetStr + "methodCount = " + str(self._methodCount))
320         print(offsetStr + "moduleDepCount = " + str(self._moduleDepCount))
321         print(offsetStr + "shortCounters = " + str(self._shortCounters))
322         print(offsetStr + "longCounters = " + str(self._longCounters))
323
324     # Print pretty
325     def print(self, offsetStr=""):
326         if (offsetStr is None or not issubclass(type(offsetStr), str)):
327             raise InternalError()
328
329         print(offsetStr + ">>> MultiCoreJit profile (version: " + str(self._version)
330                         + ", time stamp: " + str(self._timeStamp) + ")")
331         print("")
332         print(offsetStr + "Number of used modules: " + str(self._moduleCount))
333         print(offsetStr + "Number of method dependencies: " + str(self._methodCount))
334         print(offsetStr + "Number of module dependencies: " + str(self._moduleDepCount))
335         print("")
336         print(offsetStr + ">>> Stats for played profile (these are 0 if profile was not played):")
337         print("")
338         print(offsetStr + "Total number of methods: "
339                         + str(self._shortCounters[0]))
340         print(offsetStr + "Number of methods, which had native code (i.e. no jit in mcj thread for them): "
341                         + str(self._shortCounters[1]))
342         print(offsetStr + "Number of methods, which were tried to be jitted in mcj thread: "
343                         + str(self._shortCounters[2]))
344         print(offsetStr + "Number of methods, which were successfully jitted in mcj thread: "
345                         + str(self._shortCounters[3]))
346         print(offsetStr + "Number of methods, jit code for which was used after jit in mcj thread: "
347                         + str(self._shortCounters[4]))
348         print(offsetStr + "Number of methods, which were skipped for some reason: "
349                         + str(self._shortCounters[5]))
350         print(offsetStr + "Number of methods, which were skipped because of missing module: "
351                         + str(self._shortCounters[6]))
352         print(offsetStr + "Number of methods, which were traversed backwards: "
353                         + str(self._shortCounters[9]))
354         print("")
355
356     # Create from bytes
357     @staticmethod
358     def createFromBytes(bytesArr):
359         if (bytesArr is None or not issubclass(type(bytesArr), list)):
360             raise InternalError()
361
362         if (len(bytesArr) != HeaderRecord.Size):
363             raise ProfileInconsistencyError()
364
365         index = 0
366
367         recordId = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.int])
368         index += RuntimeTypeSizes.int
369
370         version = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.int])
371         index += RuntimeTypeSizes.int
372
373         timeStamp = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.int])
374         index += RuntimeTypeSizes.int
375
376         moduleCount = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.int])
377         index += RuntimeTypeSizes.int
378
379         methodCount = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.int])
380         index += RuntimeTypeSizes.int
381
382         moduleDepCount = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.int])
383         index += RuntimeTypeSizes.int
384
385         shortCounters = []
386         for i in range(0, RuntimeConstants.HEADER_W_COUNTER): # pylint: disable=unused-variable
387             shortCounters.append(Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.short]))
388             index += RuntimeTypeSizes.short
389
390         index = Utility.alignUp(index, HeaderRecord.Alignment)
391
392         longCounters = []
393         for i in range(0, RuntimeConstants.HEADER_D_COUNTER): # pylint: disable=unused-variable
394             longCounters.append(Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.int]))
395             index += RuntimeTypeSizes.int
396
397         return HeaderRecord.createByRef(recordId, version, timeStamp, moduleCount, methodCount,
398                                         moduleDepCount, shortCounters, longCounters)
399
400     # Create from objects taking them by reference
401     @staticmethod
402     def createByRef(recordId, version, timeStamp, moduleCount, methodCount,
403                     moduleDepCount, shortCounters, longCounters):
404         header = HeaderRecord()
405
406         header._recordId = recordId # pylint: disable=protected-access
407         header._version = version # pylint: disable=protected-access
408         header._timeStamp = timeStamp # pylint: disable=protected-access
409         header._moduleCount = moduleCount # pylint: disable=protected-access
410         header._methodCount = methodCount # pylint: disable=protected-access
411         header._moduleDepCount = moduleDepCount # pylint: disable=protected-access
412         header._shortCounters = shortCounters # pylint: disable=protected-access
413         header._longCounters = longCounters # pylint: disable=protected-access
414
415         header.verify()
416         return header
417
418     # Create from objects taking them by copy
419     @staticmethod
420     def createByCopy(recordId, version, timeStamp, moduleCount, methodCount,
421                      moduleDepCount, shortCounters, longCounters):
422         return HeaderRecord.createByRef(recordId, version, timeStamp, moduleCount, methodCount,
423                                         moduleDepCount, shortCounters.copy(), longCounters.copy())
424
425     # Copy object
426     def copy(self):
427         return HeaderRecord.createByCopy(self._recordId, self._version, self._timeStamp,
428                                          self._moduleCount, self._methodCount, self._moduleDepCount,
429                                          self._shortCounters, self._longCounters)
430
431     # Convert object to list of bytes
432     def convertToBytes(self):
433         index = 0
434
435         bytesArr = []
436
437         bytesArr += Utility.splitNBytes(self._recordId, RuntimeTypeSizes.int)
438         index += RuntimeTypeSizes.int
439
440         bytesArr += Utility.splitNBytes(self._version, RuntimeTypeSizes.int)
441         index += RuntimeTypeSizes.int
442
443         bytesArr += Utility.splitNBytes(self._timeStamp, RuntimeTypeSizes.int)
444         index += RuntimeTypeSizes.int
445
446         bytesArr += Utility.splitNBytes(self._moduleCount, RuntimeTypeSizes.int)
447         index += RuntimeTypeSizes.int
448
449         bytesArr += Utility.splitNBytes(self._methodCount, RuntimeTypeSizes.int)
450         index += RuntimeTypeSizes.int
451
452         bytesArr += Utility.splitNBytes(self._moduleDepCount, RuntimeTypeSizes.int)
453         index += RuntimeTypeSizes.int
454
455         for i in self._shortCounters:
456             bytesArr += Utility.splitNBytes(i, RuntimeTypeSizes.short)
457             index += RuntimeTypeSizes.short
458
459         padding = Utility.alignUp(index, HeaderRecord.Alignment) - index
460         bytesArr += [0] * padding
461         index += padding
462
463         for i in self._longCounters:
464             bytesArr += Utility.splitNBytes(i, RuntimeTypeSizes.int)
465             index += RuntimeTypeSizes.int
466
467         if (index != HeaderRecord.Size):
468             raise InternalError()
469
470         return bytesArr
471
472 # ----------------------------------------------------------------------------------------------------------------------
473 # GUID.
474 #
475 # Corresponds to "GUID" from pal_mstypes.h
476 #
477 # To create from bytes: GUID.createFromBytes(bytes)
478 # To create from data structures using references: GUID.createByRef(...)
479 # To create from data structures using copy: GUID.createByCopy(...)
480 #
481 class GUID:
482     Alignment = RuntimeTypeSizes.int
483
484     Size = RuntimeTypeSizes.int + 2 * RuntimeTypeSizes.short + 8 * RuntimeTypeSizes.char
485
486     # Empty constructor, do not use it directly
487     # Create new objects by createByCopy or createByRef
488     def __init__(self):
489         self._data1 = None
490         self._data2 = None
491         self._data3 = None
492         self._data4 = None
493
494     # Equality comparison operator
495     def __eq__(self, rhs):
496         if (rhs is None):
497             return False
498         if (not issubclass(type(rhs), GUID)):
499             return False
500         return self._data1 == rhs._data1 and self._data2 == rhs._data2 \
501                and self._data3 == rhs._data3 and self._data4 == rhs._data4
502
503     # Verify consistency
504     def verify(self):
505         if (self._data1 is None or self._data2 is None or self._data3 is None or self._data4 is None):
506             raise InternalError()
507
508         if (not issubclass(type(self._data1), int) or not issubclass(type(self._data2), int)
509             or not issubclass(type(self._data3), int) or not issubclass(type(self._data4), list)):
510             raise InternalError()
511
512         if (self._data1 < 0 or self._data1 > Utility.getMaxForNBytes(RuntimeTypeSizes.int)
513             or self._data2 < 0 or self._data2 > Utility.getMaxForNBytes(RuntimeTypeSizes.short)
514             or self._data3 < 0 or self._data3 > Utility.getMaxForNBytes(RuntimeTypeSizes.short)
515             or len(self._data4) != 8):
516             raise InternalError()
517
518         for i in self._data4:
519             if (i < 0 or i > Utility.getMaxForNBytes(RuntimeTypeSizes.char)):
520                 raise InternalError()
521
522     # Print raw
523     def printRaw(self, offsetStr=""):
524         if (offsetStr is None or not issubclass(type(offsetStr), str)):
525             raise InternalError()
526
527         print(offsetStr + ">>> Raw GUID:")
528         print(offsetStr + "data1 = " + str(self._data1))
529         print(offsetStr + "data2 = " + str(self._data2))
530         print(offsetStr + "data3 = " + str(self._data3))
531         print(offsetStr + "data4 = " + str(self._data4))
532
533     # Print pretty
534     def print(self, offsetStr=""):
535         if (offsetStr is None or not issubclass(type(offsetStr), str)):
536             raise InternalError()
537
538         print(offsetStr + "GUID: " + str(self._data1)
539                         + "," + str(self._data2)
540                         + "," + str(self._data3)
541                         + "," + str(self._data4))
542         print(offsetStr + "")
543
544     # Create from bytes
545     @staticmethod
546     def createFromBytes(bytesArr):
547         if (bytesArr is None or not issubclass(type(bytesArr), list)):
548             raise InternalError()
549
550         if (len(bytesArr) != GUID.Size):
551             raise ProfileInconsistencyError()
552
553         index = 0
554
555         data1 = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.int])
556         index += RuntimeTypeSizes.int
557
558         data2 = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.short])
559         index += RuntimeTypeSizes.short
560
561         data3 = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.short])
562         index += RuntimeTypeSizes.short
563
564         data4 = [0] * 8
565         for i in range(0, 8):
566             data4[i] = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.char])
567             index += RuntimeTypeSizes.char
568
569         return GUID.createByRef(data1, data2, data3, data4)
570
571     # Create from objects taking them by reference
572     @staticmethod
573     def createByRef(data1, data2, data3, data4):
574         guid = GUID()
575
576         guid._data1 = data1 # pylint: disable=protected-access
577         guid._data2 = data2 # pylint: disable=protected-access
578         guid._data3 = data3 # pylint: disable=protected-access
579         guid._data4 = data4 # pylint: disable=protected-access
580
581         guid.verify()
582         return guid
583
584     # Create from objects taking them by copy
585     @staticmethod
586     def createByCopy(data1, data2, data3, data4):
587         return GUID.createByRef(data1, data2, data3, data4.copy())
588
589     # Copy object
590     def copy(self):
591         return GUID.createByCopy(self._data1, self._data2, self._data3, self._data4)
592
593     # Convert object to list of bytes
594     def convertToBytes(self):
595         index = 0
596
597         bytesArr = []
598
599         bytesArr += Utility.splitNBytes(self._data1, RuntimeTypeSizes.int)
600         index += RuntimeTypeSizes.int
601
602         bytesArr += Utility.splitNBytes(self._data2, RuntimeTypeSizes.short)
603         index += RuntimeTypeSizes.short
604
605         bytesArr += Utility.splitNBytes(self._data3, RuntimeTypeSizes.short)
606         index += RuntimeTypeSizes.short
607
608         for i in self._data4:
609             bytesArr += Utility.splitNBytes(i, RuntimeTypeSizes.char)
610             index += RuntimeTypeSizes.char
611
612         if (index != GUID.Size):
613             raise InternalError()
614
615         return bytesArr
616
617 # ----------------------------------------------------------------------------------------------------------------------
618 # Module version.
619 #
620 # Corresponds to "class ModuleVersion" from multicorejitimpl.h
621 #
622 # To create from bytes: ModuleVersion.createFromBytes(bytes)
623 # To create from data structures using references: ModuleVersion.createByRef(...)
624 # To create from data structures using copy: ModuleVersion.createByCopy(...)
625 #
626 class ModuleVersion:
627     Alignment = RuntimeTypeSizes.int
628
629     Size = 4 * RuntimeTypeSizes.short + RuntimeTypeSizes.int + GUID.Size
630
631     # Empty constructor, do not use it directly
632     # Create new objects by createByCopy or createByRef
633     def __init__(self):
634         self._major = None
635         self._minor = None
636         self._build = None
637         self._revision = None
638         self._versionFlags = None
639         self._hasNativeImage = None
640         self._mvid = None
641
642     # Equality comparison operator
643     def __eq__(self, rhs):
644         if (rhs is None):
645             return False
646         if (not issubclass(type(rhs), ModuleVersion)):
647             return False
648         return self._major == rhs._major and self._minor == rhs._minor and self._build == rhs._build \
649                and self._revision == rhs._revision and self._versionFlags == rhs._versionFlags \
650                and self._hasNativeImage == rhs._hasNativeImage and self._mvid == rhs._mvid
651
652     # Verify consistency
653     def verify(self):
654         if (self._major is None or self._minor is None or self._build is None or self._revision is None
655             or self._versionFlags is None or self._hasNativeImage is None or self._mvid is None):
656             raise InternalError()
657
658         if (not issubclass(type(self._major), int) or not issubclass(type(self._minor), int)
659             or not issubclass(type(self._build), int) or not issubclass(type(self._revision), int)
660             or not issubclass(type(self._versionFlags), int) or not issubclass(type(self._hasNativeImage), int)
661             or not issubclass(type(self._mvid), GUID)):
662             raise InternalError()
663
664         if (self._major < 0 or self._major > Utility.getMaxForNBytes(RuntimeTypeSizes.short)
665             or self._minor < 0 or self._minor > Utility.getMaxForNBytes(RuntimeTypeSizes.short)
666             or self._build < 0 or self._build > Utility.getMaxForNBytes(RuntimeTypeSizes.short)
667             or self._revision < 0 or self._revision > Utility.getMaxForNBytes(RuntimeTypeSizes.short)
668             or self._versionFlags < 0 or self._versionFlags > Utility.getMaxForNBits(RuntimeTypeSizes.int * 8 - 1)
669             or self._hasNativeImage < 0 or self._hasNativeImage > Utility.getMaxForNBits(1)):
670             raise InternalError()
671
672     # Print raw
673     def printRaw(self, offsetStr=""):
674         if (offsetStr is None or not issubclass(type(offsetStr), str)):
675             raise InternalError()
676
677         print(offsetStr + ">>> Raw ModuleVersion:")
678         print(offsetStr + "major = " + str(self._major))
679         print(offsetStr + "minor = " + str(self._minor))
680         print(offsetStr + "build = " + str(self._build))
681         print(offsetStr + "revision = " + str(self._revision))
682         print(offsetStr + "versionFlags = " + str(self._versionFlags))
683         print(offsetStr + "hasNativeImage = " + str(self._hasNativeImage))
684         print(offsetStr + "mvid = {")
685         self._mvid.printRaw(offsetStr + "  ")
686         print(offsetStr + "}")
687
688     # Print pretty
689     def print(self, offsetStr=""):
690         if (offsetStr is None or not issubclass(type(offsetStr), str)):
691             raise InternalError()
692
693         print(offsetStr + "ModuleVersion: " + str(self._major)
694                         + "." + str(self._minor)
695                         + "." + str(self._build)
696                         + "." + str(self._revision)
697                         + ", " + str(self._versionFlags)
698                         + ", " + str(self._hasNativeImage))
699         self._mvid.print(offsetStr)
700
701     # Create from bytes
702     @staticmethod
703     def createFromBytes(bytesArr):
704         if (bytesArr is None or not issubclass(type(bytesArr), list)):
705             raise InternalError()
706
707         if (len(bytesArr) != ModuleVersion.Size):
708             raise ProfileInconsistencyError()
709
710         index = 0
711
712         major = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.short])
713         index += RuntimeTypeSizes.short
714
715         minor = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.short])
716         index += RuntimeTypeSizes.short
717
718         build = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.short])
719         index += RuntimeTypeSizes.short
720
721         revision = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.short])
722         index += RuntimeTypeSizes.short
723
724         versionFlags = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.int])
725         hasNativeImage = versionFlags & 0x01
726         versionFlags = versionFlags & 0xfffffffe
727         index += RuntimeTypeSizes.int
728
729         mvid = GUID.createFromBytes(bytesArr[index:index+GUID.Size])
730         index += GUID.Size
731
732         return ModuleVersion.createByRef(major, minor, build, revision, versionFlags, hasNativeImage, mvid)
733
734     # Create from objects taking them by reference
735     @staticmethod
736     def createByRef(major, minor, build, revision, versionFlags, hasNativeImage, mvid):
737         moduleVersion = ModuleVersion()
738
739         moduleVersion._major = major # pylint: disable=protected-access
740         moduleVersion._minor = minor # pylint: disable=protected-access
741         moduleVersion._build = build # pylint: disable=protected-access
742         moduleVersion._revision = revision # pylint: disable=protected-access
743         moduleVersion._versionFlags = versionFlags # pylint: disable=protected-access
744         moduleVersion._hasNativeImage = hasNativeImage # pylint: disable=protected-access
745         moduleVersion._mvid = mvid # pylint: disable=protected-access
746
747         moduleVersion.verify()
748         return moduleVersion
749
750     # Create from objects taking them by copy
751     @staticmethod
752     def createByCopy(major, minor, build, revision, versionFlags, hasNativeImage, mvid):
753         return ModuleVersion.createByRef(major, minor, build, revision, versionFlags, hasNativeImage, mvid.copy())
754
755     # Copy object
756     def copy(self):
757         return ModuleVersion.createByCopy(self._major, self._minor, self._build, self._revision,
758                                           self._versionFlags, self._hasNativeImage, self._mvid)
759
760     # Convert object to list of bytes
761     def convertToBytes(self):
762         index = 0
763
764         bytesArr = []
765
766         bytesArr += Utility.splitNBytes(self._major, RuntimeTypeSizes.short)
767         index += RuntimeTypeSizes.short
768
769         bytesArr += Utility.splitNBytes(self._minor, RuntimeTypeSizes.short)
770         index += RuntimeTypeSizes.short
771
772         bytesArr += Utility.splitNBytes(self._build, RuntimeTypeSizes.short)
773         index += RuntimeTypeSizes.short
774
775         bytesArr += Utility.splitNBytes(self._revision, RuntimeTypeSizes.short)
776         index += RuntimeTypeSizes.short
777
778         versionFlags = self._versionFlags | self._hasNativeImage
779         bytesArr += Utility.splitNBytes(versionFlags, RuntimeTypeSizes.int)
780         index += RuntimeTypeSizes.int
781
782         bytesArr += self._mvid.convertToBytes()
783         index += GUID.Size
784
785         if (index != ModuleVersion.Size):
786             raise InternalError()
787
788         return bytesArr
789
790 # ----------------------------------------------------------------------------------------------------------------------
791 # Record with information about used module.
792 #
793 # Corresponds to "struct ModuleRecord" from multicorejitimpl.h
794 # ModuleRecord in runtime implicitly "contains" module name and assembly name strings after itself,
795 # here they are a part of ModuleRecordExtended.
796 #
797 # To create from bytes: ModuleRecord.createFromBytes(bytes)
798 # To create from data structures using references: ModuleRecord.createByRef(...)
799 # To create from data structures using copy: ModuleRecord.createByCopy(...)
800 #
801 class ModuleRecord: # pylint: disable=too-many-public-methods
802     Alignment = RuntimeTypeSizes.int
803
804     Size = RuntimeTypeSizes.int + ModuleVersion.Size + Utility.alignUp(5 * RuntimeTypeSizes.short, Alignment)
805
806     # Empty constructor, do not use it directly
807     # Create new objects by createByCopy or createByRef
808     def __init__(self):
809         self._recordId = None
810         self._version = None
811         self._jitMethodCount = None
812         self._flags = None
813         self._wLoadLevel = None
814         self._lenModuleName = None
815         self._lenAssemblyName = None
816
817     # Equality comparison operator
818     def __eq__(self, rhs):
819         if (rhs is None):
820             return False
821         if (not issubclass(type(rhs), ModuleRecord)):
822             return False
823         return self._recordId == rhs._recordId and self._version == rhs._version \
824                and self._jitMethodCount == rhs._jitMethodCount and self._flags == rhs._flags \
825                and self._wLoadLevel == rhs._wLoadLevel and self._lenModuleName == rhs._lenModuleName \
826                and self._lenAssemblyName == rhs._lenAssemblyName
827
828     # Get recordId
829     def getRecordId(self):
830         return self._recordId
831
832     # Get version
833     def getVersion(self):
834         return self._version
835
836     # Get jitMethodCount
837     def getJitMethodCount(self):
838         return self._jitMethodCount
839
840     # Get flags
841     def getFlags(self):
842         return self._flags
843
844     # Get wLoadLevel
845     def getLoadLevel(self):
846         return self._wLoadLevel
847
848     # Get lenModuleName
849     def getLenModuleName(self):
850         return self._lenModuleName
851
852     # Get lenAssemblyName
853     def getLenAssemblyName(self):
854         return self._lenAssemblyName
855
856     # Set jitMethodCount
857     def setJitMethodCount(self, count):
858         self._jitMethodCount = count
859         self.verify()
860
861     # Set wLoadLevel
862     def setLoadLevel(self, level):
863         self._wLoadLevel = level
864         self.verify()
865
866     # Verify consistency
867     def verify(self):
868         if (self._recordId is None or self._version is None or self._jitMethodCount is None or self._flags is None
869             or self._wLoadLevel is None or self._lenModuleName is None or self._lenAssemblyName is None):
870             raise InternalError()
871
872         if (not issubclass(type(self._recordId), int) or not issubclass(type(self._version), ModuleVersion)
873             or not issubclass(type(self._jitMethodCount), int) or not issubclass(type(self._flags), int)
874             or not issubclass(type(self._wLoadLevel), int) or not issubclass(type(self._lenModuleName), int)
875             or not issubclass(type(self._lenAssemblyName), int)):
876             raise InternalError()
877
878         if (self._recordId < 0 or self._recordId > Utility.getMaxForNBytes(RuntimeTypeSizes.int)
879             or self._jitMethodCount < 0 or self._jitMethodCount > Utility.getMaxForNBytes(RuntimeTypeSizes.short)
880             or self._flags < 0 or self._flags > Utility.getMaxForNBytes(RuntimeTypeSizes.short)
881             or self._wLoadLevel < 0 or self._wLoadLevel > Utility.getMaxForNBytes(RuntimeTypeSizes.short)
882             or self._lenModuleName < 0 or self._lenModuleName > Utility.getMaxForNBytes(RuntimeTypeSizes.short)
883             or self._lenAssemblyName < 0 or self._lenAssemblyName > Utility.getMaxForNBytes(RuntimeTypeSizes.short)):
884             raise InternalError()
885
886     # Print raw
887     def printRaw(self, offsetStr=""):
888         if (offsetStr is None or not issubclass(type(offsetStr), str)):
889             raise InternalError()
890
891         print(offsetStr + ">>> Raw ModuleRecord:")
892         print(offsetStr + "recordId = " + str(self._recordId))
893         print(offsetStr + "version = {")
894         self._version.printRaw(offsetStr + "  ")
895         print(offsetStr + "}")
896         print(offsetStr + "jitMethodCount = " + str(self._jitMethodCount))
897         print(offsetStr + "flags = " + str(self._flags))
898         print(offsetStr + "wLoadLevel = " + str(self._wLoadLevel))
899         print(offsetStr + "lenModuleName = " + str(self._lenModuleName))
900         print(offsetStr + "lenAssemblyName = " + str(self._lenAssemblyName))
901
902     # Print pretty
903     def print(self, offsetStr=""):
904         if (offsetStr is None or not issubclass(type(offsetStr), str)):
905             raise InternalError()
906
907         self._version.print(offsetStr)
908         print(offsetStr + "Number of used methods from module: " + str(self._jitMethodCount))
909         print(offsetStr + "Final load level for module: " + str(self._wLoadLevel))
910
911     # Create from bytes
912     @staticmethod
913     def createFromBytes(bytesArr):
914         if (bytesArr is None or not issubclass(type(bytesArr), list)):
915             raise InternalError()
916
917         if (len(bytesArr) != ModuleRecord.Size):
918             raise ProfileInconsistencyError()
919
920         index = 0
921
922         recordId = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.int])
923         index += RuntimeTypeSizes.int
924
925         version = ModuleVersion.createFromBytes(bytesArr[index:index+ModuleVersion.Size])
926         index += ModuleVersion.Size
927
928         jitMethodCount = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.short])
929         index += RuntimeTypeSizes.short
930
931         flags = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.short])
932         index += RuntimeTypeSizes.short
933
934         wLoadLevel = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.short])
935         index += RuntimeTypeSizes.short
936
937         lenModuleName = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.short])
938         index += RuntimeTypeSizes.short
939
940         lenAssemblyName = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.short])
941         index += RuntimeTypeSizes.short
942
943         return ModuleRecord.createByRef(recordId, version, jitMethodCount, flags, wLoadLevel,
944                                         lenModuleName, lenAssemblyName)
945
946     # Create from objects taking them by reference
947     @staticmethod
948     def createByRef(recordId, version, jitMethodCount, flags, wLoadLevel, lenModuleName, lenAssemblyName):
949         moduleRecord = ModuleRecord()
950
951         moduleRecord._recordId = recordId # pylint: disable=protected-access
952         moduleRecord._version = version # pylint: disable=protected-access
953         moduleRecord._jitMethodCount = jitMethodCount # pylint: disable=protected-access
954         moduleRecord._flags = flags # pylint: disable=protected-access
955         moduleRecord._wLoadLevel = wLoadLevel # pylint: disable=protected-access
956         moduleRecord._lenModuleName = lenModuleName # pylint: disable=protected-access
957         moduleRecord._lenAssemblyName = lenAssemblyName # pylint: disable=protected-access
958
959         moduleRecord.verify()
960         return moduleRecord
961
962     # Create from objects taking them by copy
963     @staticmethod
964     def createByCopy(recordId, version, jitMethodCount, flags, wLoadLevel, lenModuleName, lenAssemblyName):
965         return ModuleRecord.createByRef(recordId, version.copy(), jitMethodCount, flags, wLoadLevel,
966                                         lenModuleName, lenAssemblyName)
967
968     # Copy object
969     def copy(self):
970         return ModuleRecord.createByCopy(self._recordId, self._version, self._jitMethodCount,
971                                          self._flags, self._wLoadLevel,
972                                          self._lenModuleName, self._lenAssemblyName)
973
974     # Convert object to list of bytes
975     def convertToBytes(self):
976         index = 0
977
978         bytesArr = []
979
980         bytesArr += Utility.splitNBytes(self._recordId, RuntimeTypeSizes.int)
981         index += RuntimeTypeSizes.int
982
983         bytesArr += self._version.convertToBytes()
984         index += ModuleVersion.Size
985
986         bytesArr += Utility.splitNBytes(self._jitMethodCount, RuntimeTypeSizes.short)
987         index += RuntimeTypeSizes.short
988
989         bytesArr += Utility.splitNBytes(self._flags, RuntimeTypeSizes.short)
990         index += RuntimeTypeSizes.short
991
992         bytesArr += Utility.splitNBytes(self._wLoadLevel, RuntimeTypeSizes.short)
993         index += RuntimeTypeSizes.short
994
995         bytesArr += Utility.splitNBytes(self._lenModuleName, RuntimeTypeSizes.short)
996         index += RuntimeTypeSizes.short
997
998         bytesArr += Utility.splitNBytes(self._lenAssemblyName, RuntimeTypeSizes.short)
999         index += RuntimeTypeSizes.short
1000
1001         padding = Utility.alignUp(index, HeaderRecord.Alignment) - index
1002         bytesArr += [0] * padding
1003         index += padding
1004
1005         if (index != ModuleRecord.Size):
1006             raise InternalError()
1007
1008         return bytesArr
1009
1010 # ----------------------------------------------------------------------------------------------------------------------
1011 # Record with information about used module.
1012 #
1013 # This is ModuleRecord combined with actual module/assembly name strings.
1014 #
1015 # To create from bytes: ModuleRecordExtended.createFromBytes(bytes)
1016 # To create from data structures using references: ModuleRecordExtended.createByRef(...)
1017 # To create from data structures using copy: ModuleRecordExtended.createByCopy(...)
1018 #
1019 class ModuleRecordExtended:
1020
1021     # Empty constructor, do not use it directly
1022     # Create new objects by createByCopy or createByRef
1023     def __init__(self):
1024         self._moduleRecord = None
1025         self._moduleName = None
1026         self._assemblyName = None
1027
1028     # Equality comparison operator
1029     def __eq__(self, rhs):
1030         if (rhs is None):
1031             return False
1032         if (not issubclass(type(rhs), ModuleRecordExtended)):
1033             return False
1034
1035         return self._moduleRecord == rhs._moduleRecord and \
1036                self._moduleName == rhs._moduleName and self._assemblyName == rhs._assemblyName
1037
1038     # Get moduleRecord
1039     def getModuleRecord(self):
1040         return self._moduleRecord
1041
1042     # Get moduleName
1043     def getModuleName(self):
1044         return self._moduleName
1045
1046     # Get assemblyName
1047     def getAssemblyName(self):
1048         return self._assemblyName
1049
1050     # Verify consistency
1051     def verify(self):
1052         if (self._moduleRecord is None or self._moduleName is None or self._assemblyName is None):
1053             raise InternalError()
1054
1055         if (not issubclass(type(self._moduleRecord), ModuleRecord)
1056             or not issubclass(type(self._moduleName), str)
1057             or not issubclass(type(self._assemblyName), str)):
1058             raise InternalError()
1059
1060         if (len(self._moduleName) != self._moduleRecord.getLenModuleName()
1061             or len(self._assemblyName) != self._moduleRecord.getLenAssemblyName()):
1062             raise InternalError()
1063
1064     # Print raw
1065     def printRaw(self, offsetStr=""):
1066         if (offsetStr is None or not issubclass(type(offsetStr), str)):
1067             raise InternalError()
1068
1069         print(offsetStr + "moduleRecord = {")
1070         self._moduleRecord.printRaw(offsetStr + "  ")
1071         print(offsetStr + "}")
1072         print(offsetStr + "moduleName = " + self._moduleName)
1073         print(offsetStr + "assemblyName = " + self._assemblyName)
1074
1075     # Print pretty
1076     def print(self, offsetStr=""):
1077         if (offsetStr is None or not issubclass(type(offsetStr), str)):
1078             raise InternalError()
1079
1080         print(offsetStr + ">>> Module: " + self._moduleName + "; Assembly: " + self._assemblyName)
1081         print(offsetStr + "")
1082         self._moduleRecord.print(offsetStr)
1083         print(offsetStr + "")
1084
1085     # Create from bytes
1086     @staticmethod
1087     def createFromBytes(bytesArr):
1088         if (bytesArr is None or not issubclass(type(bytesArr), list)):
1089             raise InternalError()
1090
1091         if (len(bytesArr) < ModuleRecord.Size):
1092             raise ProfileInconsistencyError()
1093
1094         index = 0
1095
1096         moduleRecord = ModuleRecord.createFromBytes(bytesArr[index:index+ModuleRecord.Size])
1097         index += ModuleRecord.Size
1098
1099         lenModuleName = moduleRecord.getLenModuleName()
1100         lenModuleNameAligned = Utility.alignUp(lenModuleName, RuntimeTypeSizes.int)
1101
1102         lenAssemblyName = moduleRecord.getLenAssemblyName()
1103         lenAssemblyNameAligned = Utility.alignUp(lenAssemblyName, RuntimeTypeSizes.int)
1104
1105         if (len(bytesArr) != (ModuleRecord.Size + lenModuleNameAligned + lenAssemblyNameAligned)):
1106             raise ProfileInconsistencyError()
1107
1108         moduleName = ""
1109         for i in bytesArr[index:index+lenModuleName]:
1110             moduleName += chr(i)
1111         index += lenModuleNameAligned
1112
1113         assemblyName = ""
1114         for i in bytesArr[index:index+lenAssemblyName]:
1115             assemblyName += chr(i)
1116         index += lenAssemblyNameAligned
1117
1118         if (index != (ModuleRecord.Size + lenModuleNameAligned + lenAssemblyNameAligned)):
1119             raise InternalError()
1120
1121         return ModuleRecordExtended.createByRef(moduleRecord, moduleName, assemblyName)
1122
1123     # Create from objects taking them by reference
1124     @staticmethod
1125     def createByRef(moduleRecord, moduleName, assemblyName):
1126         moduleRecordExtended = ModuleRecordExtended()
1127
1128         moduleRecordExtended._moduleRecord = moduleRecord # pylint: disable=protected-access
1129         moduleRecordExtended._moduleName = moduleName # pylint: disable=protected-access
1130         moduleRecordExtended._assemblyName = assemblyName # pylint: disable=protected-access
1131
1132         moduleRecordExtended.verify()
1133         return moduleRecordExtended
1134
1135     # Create from objects taking them by copy
1136     @staticmethod
1137     def createByCopy(moduleRecord, moduleName, assemblyName):
1138         return ModuleRecordExtended.createByRef(moduleRecord.copy(), moduleName, assemblyName)
1139
1140     # Copy object
1141     def copy(self):
1142         return ModuleRecordExtended.createByCopy(self._moduleRecord, self._moduleName, self._assemblyName)
1143
1144     # Convert object to list of bytes
1145     def convertToBytes(self):
1146         index = 0
1147
1148         bytesArr = []
1149
1150         bytesArr += self._moduleRecord.convertToBytes()
1151         index += ModuleRecord.Size
1152
1153         lenModuleName = self._moduleRecord.getLenModuleName()
1154         lenModuleNameAligned = Utility.alignUp(lenModuleName, RuntimeTypeSizes.int)
1155         bytesArr += list(bytearray(self._moduleName, "ascii"))
1156         index += lenModuleName
1157
1158         padding = lenModuleNameAligned - lenModuleName
1159         bytesArr += [0] * padding
1160         index += padding
1161
1162         lenAssemblyName = self._moduleRecord.getLenAssemblyName()
1163         lenAssemblyNameAligned = Utility.alignUp(lenAssemblyName, RuntimeTypeSizes.int)
1164         bytesArr += list(bytearray(self._assemblyName, "ascii"))
1165         index += lenAssemblyName
1166
1167         padding = lenAssemblyNameAligned - lenAssemblyName
1168         bytesArr += [0] * padding
1169         index += padding
1170
1171         if (index != (ModuleRecord.Size + lenModuleNameAligned + lenAssemblyNameAligned)):
1172             raise InternalError()
1173
1174         return bytesArr
1175
1176 # ----------------------------------------------------------------------------------------------------------------------
1177 # Detailed info on types
1178 #
1179 # Semantically corresponds to CorTypeInfo
1180 #
1181 class CorTypeInfo:
1182
1183     # Kinds of types
1184     #
1185     # Semantically corresponds to CorElementType
1186     # TODO: verify these constants somehow, see above
1187     class CorElementType: # pylint: disable=too-few-public-methods
1188
1189         ELEMENT_TYPE_END            = 0x00
1190         ELEMENT_TYPE_VOID           = 0x01
1191         ELEMENT_TYPE_BOOLEAN        = 0x02
1192         ELEMENT_TYPE_CHAR           = 0x03
1193         ELEMENT_TYPE_I1             = 0x04
1194         ELEMENT_TYPE_U1             = 0x05
1195         ELEMENT_TYPE_I2             = 0x06
1196         ELEMENT_TYPE_U2             = 0x07
1197         ELEMENT_TYPE_I4             = 0x08
1198         ELEMENT_TYPE_U4             = 0x09
1199         ELEMENT_TYPE_I8             = 0x0a
1200         ELEMENT_TYPE_U8             = 0x0b
1201         ELEMENT_TYPE_R4             = 0x0c
1202         ELEMENT_TYPE_R8             = 0x0d
1203         ELEMENT_TYPE_STRING         = 0x0e
1204
1205         ELEMENT_TYPE_PTR            = 0x0f
1206         ELEMENT_TYPE_BYREF          = 0x10
1207
1208         ELEMENT_TYPE_VALUETYPE      = 0x11
1209         ELEMENT_TYPE_CLASS          = 0x12
1210         ELEMENT_TYPE_VAR            = 0x13
1211         ELEMENT_TYPE_ARRAY          = 0x14
1212         ELEMENT_TYPE_GENERICINST    = 0x15
1213         ELEMENT_TYPE_TYPEDBYREF     = 0x16
1214         ELEMENT_TYPE_VALUEARRAY_UNSUPPORTED = 0x17
1215         ELEMENT_TYPE_I              = 0x18
1216         ELEMENT_TYPE_U              = 0x19
1217         ELEMENT_TYPE_R_UNSUPPORTED  = 0x1a
1218         ELEMENT_TYPE_FNPTR          = 0x1b
1219         ELEMENT_TYPE_OBJECT         = 0x1c
1220         ELEMENT_TYPE_SZARRAY        = 0x1d
1221         ELEMENT_TYPE_MVAR           = 0x1e
1222
1223         ELEMENT_TYPE_CMOD_REQD      = 0x1f
1224         ELEMENT_TYPE_CMOD_OPT       = 0x20
1225
1226         ELEMENT_TYPE_INTERNAL       = 0x21
1227
1228         ELEMENT_TYPE_MAX            = 0x22
1229
1230         ELEMENT_TYPE_VAR_ZAPSIG     = 0x3b
1231         ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG = 0x3d
1232         ELEMENT_TYPE_CANON_ZAPSIG   = 0x3e
1233         ELEMENT_TYPE_MODULE_ZAPSIG  = 0x3f
1234
1235         ELEMENT_TYPE_MODIFIER       = 0x40
1236         ELEMENT_TYPE_SENTINEL       = 0x01 | ELEMENT_TYPE_MODIFIER
1237         ELEMENT_TYPE_PINNED         = 0x05 | ELEMENT_TYPE_MODIFIER
1238
1239         # This constant is not defined in runtime
1240         X_ELEMENT_TYPE_LAST         = ELEMENT_TYPE_PINNED + 1
1241
1242     # Entry in map of types info
1243     class Entry:
1244
1245         # Default constructor
1246         def __init__(self, elementKind, name, isPrim):
1247             self._elementKind = elementKind
1248             self._name = name
1249             self._isPrim = isPrim
1250
1251         # Get kind of record
1252         def getKind(self):
1253             return self._elementKind
1254
1255         # Get name of record
1256         def getName(self):
1257             return self._name
1258
1259         # Get flag whether type is primitive
1260         def getIsPrim(self):
1261             return self._isPrim
1262
1263     # Fill map with types info
1264     @staticmethod
1265     def fillMap():
1266         tmpmap = [None] * CorTypeInfo.CorElementType.X_ELEMENT_TYPE_LAST
1267         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_END] = \
1268           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_END, "ELEMENT_TYPE_END", False)
1269         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_VOID] = \
1270           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_VOID, "void", True)
1271         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_BOOLEAN] = \
1272           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_BOOLEAN, "bool", True)
1273         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_CHAR] = \
1274           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_CHAR, "char", True)
1275         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_I1] = \
1276           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_I1, "i1", True)
1277         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_U1] = \
1278           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_U1, "u1", True)
1279         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_I2] = \
1280           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_I2, "i2", True)
1281         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_U2] = \
1282           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_U2, "u2", True)
1283         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_I4] = \
1284           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_I4, "i4", True)
1285         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_U4] = \
1286           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_U4, "u4", True)
1287         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_I8] = \
1288           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_I8, "i8", True)
1289         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_U8] = \
1290           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_U8, "u8", True)
1291         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_R4] = \
1292           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_R4, "r4", True)
1293         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_R8] = \
1294           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_R8, "r8", True)
1295         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_STRING] = \
1296           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_STRING, "string", False)
1297         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_PTR] = \
1298           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_PTR, "ptr", False)
1299         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_BYREF] = \
1300           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_BYREF, "byref", False)
1301         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_VALUETYPE] = \
1302           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_VALUETYPE, "valuetype", False)
1303         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_CLASS] = \
1304           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_CLASS, "class", False)
1305         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_VAR] = \
1306           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_VAR, "var", False)
1307         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_ARRAY] = \
1308           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_ARRAY, "array", False)
1309         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_GENERICINST] = \
1310           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_GENERICINST, "generic", False)
1311         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_TYPEDBYREF] = \
1312           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_TYPEDBYREF, "typedbyref", False)
1313         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_VALUEARRAY_UNSUPPORTED] = \
1314           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_VALUEARRAY_UNSUPPORTED,
1315                             "valuearray_unsupported", False)
1316         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_I] = \
1317           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_I, "i", True)
1318         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_U] = \
1319           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_U, "u", True)
1320         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_R_UNSUPPORTED] = \
1321           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_R_UNSUPPORTED, "r_unsupported", False)
1322         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_FNPTR] = \
1323           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_FNPTR, "fnptr", False)
1324         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_OBJECT] = \
1325           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_OBJECT, "object", False)
1326         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_SZARRAY] = \
1327           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_SZARRAY, "szarray", False)
1328         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_MVAR] = \
1329           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_MVAR, "mvar", False)
1330         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_CMOD_REQD] = \
1331           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_CMOD_REQD, "cmod_reqd", False)
1332         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_CMOD_OPT] = \
1333           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_CMOD_OPT, "cmod_opt", False)
1334         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_INTERNAL] = \
1335           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_INTERNAL, "internal", False)
1336         #tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_MAX] = \
1337         #  CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_MAX, "ELEMENT_TYPE_MAX", False)
1338         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_VAR_ZAPSIG] = \
1339           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_VAR_ZAPSIG, "vap_zapsig", False)
1340         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG] = \
1341           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG,
1342                             "native_valuetype_zapsig", False)
1343         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_CANON_ZAPSIG] = \
1344            CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_CANON_ZAPSIG, "canon_zapsig", False)
1345         tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_MODULE_ZAPSIG] = \
1346           CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_MODULE_ZAPSIG, "module_zapsig", False)
1347         #tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_MODIFIER] = \
1348         #  CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_MODIFIER, "ELEMENT_TYPE_MODIFIER", False)
1349         #tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_SENTINEL] = \
1350         #  CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_SENTINEL, "ELEMENT_TYPE_SENTINEL", False)
1351         #tmpmap[CorTypeInfo.CorElementType.ELEMENT_TYPE_PINNED] = \
1352         #  CorTypeInfo.Entry(CorTypeInfo.CorElementType.ELEMENT_TYPE_PINNED, "ELEMENT_TYPE_PINNED", False)
1353
1354         # verify generated map
1355         for index, record in enumerate(tmpmap):
1356             if (not record is None and record.getKind() != index):
1357                 raise InternalError()
1358
1359         return tmpmap
1360
1361     # Map with types info, is filled explicitly below.
1362     # Records that are absent from CorTypeInfo.CorElementType are None.
1363     # Use getTypeMapEntry to access typemap.
1364     typemap = None
1365
1366     # Get info for type
1367     @staticmethod
1368     def getTypeMapEntry(elementKind):
1369         if (elementKind is None or not issubclass(type(elementKind), int)):
1370             raise InternalError()
1371         record = CorTypeInfo.typemap[elementKind] # pylint: disable=unsubscriptable-object
1372         if (record is None):
1373             raise InternalError()
1374         if (record.getKind() != elementKind):
1375             raise InternalError()
1376         return record
1377
1378 # Fill map of types info
1379 CorTypeInfo.typemap = CorTypeInfo.fillMap()
1380
1381 # ----------------------------------------------------------------------------------------------------------------------
1382 # These are different kinds of types corresponding to CorTypeInfo.CorElementType
1383
1384 # Base class for all types
1385 class IType(ABC):
1386
1387     # Get element kind
1388     @abstractmethod
1389     def getElementKind(self):
1390         pass
1391
1392     # Get string representation of type
1393     @abstractmethod
1394     def getStr(self, modules=None):
1395         pass
1396
1397     # Update all module indexes according to map old_index->new_index
1398     @abstractmethod
1399     def updateModuleIndex(self, moduleIndexMap):
1400         pass
1401
1402     # Get all module indexes used in related types
1403     @abstractmethod
1404     def getAllModuleIndexes(self):
1405         pass
1406
1407     # Copy type
1408     @abstractmethod
1409     def copy(self):
1410         pass
1411
1412
1413 # Corresponding to simple types that are fully described by their CorElementType
1414 #
1415 # To create from data structures using references: TypeSimple.createByRef(...)
1416 # To create from data structures using copy: TypeSimple.createByCopy(...)
1417 #
1418 class TypeSimple(IType):
1419
1420     # Empty constructor, do not use it directly
1421     # Create new objects by createByCopy or createByRef
1422     def __init__(self):
1423         self._elementKind = None
1424
1425     # Equality comparison operator
1426     def __eq__(self, rhs):
1427         if (rhs is None):
1428             return False
1429         if (not issubclass(type(rhs), TypeSimple)):
1430             return False
1431         return self._elementKind == rhs._elementKind
1432
1433     # Convert to string
1434     def getStr(self, modules=None):
1435         if (not modules is None):
1436             if (not issubclass(type(modules), list)):
1437                 raise InternalError()
1438             for i in modules:
1439                 if (i is None or not issubclass(type(i), ModuleRecordExtended)):
1440                     raise InternalError()
1441
1442         return CorTypeInfo.getTypeMapEntry(self._elementKind).getName()
1443
1444     # Get elementKind
1445     def getElementKind(self):
1446         return self._elementKind
1447
1448     # Get all module indexes used in related types
1449     def getAllModuleIndexes(self):
1450         return []
1451
1452     # Update all module indexes according to map old_index->new_index
1453     def updateModuleIndex(self, moduleIndexMap):
1454         if (moduleIndexMap is None or not issubclass(type(moduleIndexMap), list)):
1455             raise InternalError()
1456         self.verify()
1457
1458     # Verify consistency
1459     def verify(self):
1460         if (self._elementKind is None):
1461             raise InternalError()
1462
1463         if (not issubclass(type(self._elementKind), int)):
1464             raise InternalError()
1465
1466         if (self._elementKind < 0 or self._elementKind >= CorTypeInfo.CorElementType.X_ELEMENT_TYPE_LAST):
1467             raise InternalError()
1468
1469     # Create from objects taking them by reference
1470     @staticmethod
1471     def createByRef(elementKind):
1472         handle = TypeSimple()
1473
1474         handle._elementKind = elementKind # pylint: disable=protected-access
1475
1476         handle.verify()
1477         return handle
1478
1479     # Create from objects taking them by copy
1480     @staticmethod
1481     def createByCopy(elementKind):
1482         return TypeSimple.createByRef(elementKind)
1483
1484     # Copy object
1485     def copy(self):
1486         return TypeSimple.createByCopy(self._elementKind)
1487
1488
1489 # Corresponding to types that are fully described by their CorElementType, module index and type token
1490 #
1491 # To create from data structures using references: TypeToken.createByRef(...)
1492 # To create from data structures using copy: TypeToken.createByCopy(...)
1493 #
1494 class TypeToken(IType):
1495
1496     # Empty constructor, do not use it directly
1497     # Create new objects by createByCopy or createByRef
1498     def __init__(self):
1499         self._elementKind = None
1500         self._moduleIndex = None
1501         self._typeToken = None
1502
1503     # Equality comparison operator
1504     def __eq__(self, rhs):
1505         if (rhs is None):
1506             return False
1507         if (not issubclass(type(rhs), TypeToken)):
1508             return False
1509         return self._elementKind == rhs._elementKind and self._moduleIndex == rhs._moduleIndex \
1510                and self._typeToken == rhs._typeToken
1511
1512     # Convert to string
1513     def getStr(self, modules=None):
1514         if (not modules is None):
1515             if (not issubclass(type(modules), list)):
1516                 raise InternalError()
1517             for i in modules:
1518                 if (i is None or not issubclass(type(i), ModuleRecordExtended)):
1519                     raise InternalError()
1520
1521         moduleStr = str(self._moduleIndex)
1522         if (not modules is None):
1523             moduleStr = modules[self._moduleIndex].getModuleName()
1524
1525         return CorTypeInfo.getTypeMapEntry(self._elementKind).getName() + "{" + str(self._typeToken) \
1526                + ":" + moduleStr + "}"
1527
1528     # Get elementKind
1529     def getElementKind(self):
1530         return self._elementKind
1531
1532     # Get moduleIndex
1533     def getModuleIndex(self):
1534         return self._moduleIndex
1535
1536     # Get typeToken
1537     def getTypeToken(self):
1538         return self._typeToken
1539
1540     # Get all module indexes used in related types
1541     def getAllModuleIndexes(self):
1542         return [self._moduleIndex]
1543
1544     # Update all module indexes according to map old_index->new_index
1545     def updateModuleIndex(self, moduleIndexMap):
1546         if (moduleIndexMap is None or not issubclass(type(moduleIndexMap), list)):
1547             raise InternalError()
1548         self._moduleIndex = moduleIndexMap[self._moduleIndex]
1549         self.verify()
1550
1551     # Verify consistency
1552     def verify(self):
1553         if (self._elementKind is None or self._moduleIndex is None or self._typeToken is None):
1554             raise InternalError()
1555
1556         if (not issubclass(type(self._elementKind), int) or not issubclass(type(self._moduleIndex), int)
1557             or not issubclass(type(self._typeToken), int)):
1558             raise InternalError()
1559
1560         if (self._elementKind < 0 or self._elementKind >= CorTypeInfo.CorElementType.X_ELEMENT_TYPE_LAST):
1561             raise InternalError()
1562
1563         if (self._moduleIndex < 0 or self._moduleIndex >= RuntimeConstants.MAX_MODULES):
1564             raise InternalError()
1565
1566         if (self._typeToken < 0 or self._typeToken > Utility.getMaxForNBytes(RuntimeTypeSizes.int)):
1567             raise InternalError()
1568
1569     # Create from objects taking them by reference
1570     @staticmethod
1571     def createByRef(elementKind, moduleIndex, typeToken):
1572         handle = TypeToken()
1573
1574         handle._elementKind = elementKind # pylint: disable=protected-access
1575         handle._moduleIndex = moduleIndex # pylint: disable=protected-access
1576         handle._typeToken = typeToken # pylint: disable=protected-access
1577
1578         handle.verify()
1579         return handle
1580
1581     # Create from objects taking them by copy
1582     @staticmethod
1583     def createByCopy(elementKind, moduleIndex, typeToken):
1584         return TypeToken.createByRef(elementKind, moduleIndex, typeToken)
1585
1586     # Copy object
1587     def copy(self):
1588         return TypeToken.createByCopy(self._elementKind, self._moduleIndex, self._typeToken)
1589
1590
1591 # Corresponding to simple types with param type
1592 #
1593 # To create from data structures using references: TypeParamType.createByRef(...)
1594 # To create from data structures using copy: TypeParamType.createByCopy(...)
1595 #
1596 class TypeParamType(IType):
1597
1598     # Empty constructor, do not use it directly
1599     # Create new objects by createByCopy or createByRef
1600     def __init__(self):
1601         self._elementKind = None
1602         self._paramType = None
1603
1604     # Equality comparison operator
1605     def __eq__(self, rhs):
1606         if (rhs is None):
1607             return False
1608         if (not issubclass(type(rhs), TypeParamType)):
1609             return False
1610         return self._elementKind == rhs._elementKind and self._paramType == rhs._paramType
1611
1612     # Convert to string
1613     def getStr(self, modules=None):
1614         if (not modules is None):
1615             if (not issubclass(type(modules), list)):
1616                 raise InternalError()
1617             for i in modules:
1618                 if (i is None or not issubclass(type(i), ModuleRecordExtended)):
1619                     raise InternalError()
1620
1621         return CorTypeInfo.getTypeMapEntry(self._elementKind).getName() + "{" + self._paramType.getStr(modules) + "}"
1622
1623     # Get elementKind
1624     def getElementKind(self):
1625         return self._elementKind
1626
1627     # Get paramType
1628     def getParamType(self):
1629         return self._paramType
1630
1631     # Get all module indexes used in related types
1632     def getAllModuleIndexes(self):
1633         return self._paramType.getAllModuleIndexes()
1634
1635     # Update all module indexes according to map old_index->new_index
1636     def updateModuleIndex(self, moduleIndexMap):
1637         if (moduleIndexMap is None or not issubclass(type(moduleIndexMap), list)):
1638             raise InternalError()
1639         self._paramType.updateModuleIndex(moduleIndexMap)
1640         self.verify()
1641
1642     # Verify consistency
1643     def verify(self):
1644         if (self._elementKind is None or self._paramType is None):
1645             raise InternalError()
1646
1647         if (not issubclass(type(self._elementKind), int) or not issubclass(type(self._paramType), IType)):
1648             raise InternalError()
1649
1650         if (self._elementKind < 0 or self._elementKind >= CorTypeInfo.CorElementType.X_ELEMENT_TYPE_LAST):
1651             raise InternalError()
1652
1653     # Create from objects taking them by reference
1654     @staticmethod
1655     def createByRef(elementKind, paramType):
1656         handle = TypeParamType()
1657
1658         handle._elementKind = elementKind # pylint: disable=protected-access
1659         handle._paramType = paramType # pylint: disable=protected-access
1660
1661         handle.verify()
1662         return handle
1663
1664     # Create from objects taking them by copy
1665     @staticmethod
1666     def createByCopy(elementKind, paramType):
1667         return TypeParamType.createByRef(elementKind, paramType.copy())
1668
1669     # Copy object
1670     def copy(self):
1671         return TypeParamType.createByCopy(self._elementKind, self._paramType)
1672
1673
1674 # Corresponding to array type
1675 #
1676 # To create from data structures using references: TypeArray.createByRef(...)
1677 # To create from data structures using copy: TypeArray.createByCopy(...)
1678 #
1679 class TypeArray(IType):
1680
1681     # Empty constructor, do not use it directly
1682     # Create new objects by createByCopy or createByRef
1683     def __init__(self):
1684         self._elementKind = None
1685         self._arrayElementType = None
1686         self._rank = None
1687
1688     # Equality comparison operator
1689     def __eq__(self, rhs):
1690         if (rhs is None):
1691             return False
1692         if (not issubclass(type(rhs), TypeArray)):
1693             return False
1694         return self._elementKind == rhs._elementKind and self._arrayElementType == rhs._arrayElementType \
1695                and self._rank == rhs._rank
1696
1697     # Convert to string
1698     def getStr(self, modules=None):
1699         if (not modules is None):
1700             if (not issubclass(type(modules), list)):
1701                 raise InternalError()
1702             for i in modules:
1703                 if (i is None or not issubclass(type(i), ModuleRecordExtended)):
1704                     raise InternalError()
1705
1706         return CorTypeInfo.getTypeMapEntry(self._elementKind).getName() + "{[" \
1707                + self._arrayElementType.getStr(modules) + " ]" + str(self._rank) + "}"
1708
1709     # Get elementKind
1710     def getElementKind(self):
1711         return self._elementKind
1712
1713     # Get arrayElementType
1714     def getArrayElementType(self):
1715         return self._arrayElementType
1716
1717     # Get rank
1718     def getRank(self):
1719         return self._rank
1720
1721     # Get all module indexes used in related types
1722     def getAllModuleIndexes(self):
1723         return self._arrayElementType.getAllModuleIndexes()
1724
1725     # Update all module indexes according to map old_index->new_index
1726     def updateModuleIndex(self, moduleIndexMap):
1727         if (moduleIndexMap is None or not issubclass(type(moduleIndexMap), list)):
1728             raise InternalError()
1729         self._arrayElementType.updateModuleIndex(moduleIndexMap)
1730         self.verify()
1731
1732     # Verify consistency
1733     def verify(self):
1734         if (self._elementKind is None or self._arrayElementType is None or self._rank is None):
1735             raise InternalError()
1736
1737         if (not issubclass(type(self._elementKind), int) or not issubclass(type(self._arrayElementType), IType)
1738             or not issubclass(type(self._rank), int)):
1739             raise InternalError()
1740
1741         if (self._elementKind < 0 or self._elementKind >= CorTypeInfo.CorElementType.X_ELEMENT_TYPE_LAST):
1742             raise InternalError()
1743
1744         if (self._rank < 0 or self._rank > Utility.getMaxForNBytes(RuntimeTypeSizes.char)):
1745             raise InternalError()
1746
1747     # Create from objects taking them by reference
1748     @staticmethod
1749     def createByRef(elementKind, arrayElementType, rank):
1750         handle = TypeArray()
1751
1752         handle._elementKind = elementKind # pylint: disable=protected-access
1753         handle._arrayElementType = arrayElementType # pylint: disable=protected-access
1754         handle._rank = rank # pylint: disable=protected-access
1755
1756         handle.verify()
1757         return handle
1758
1759     # Create from objects taking them by copy
1760     @staticmethod
1761     def createByCopy(elementKind, arrayElementType, rank):
1762         return TypeArray.createByRef(elementKind, arrayElementType.copy(), rank)
1763
1764     # Copy object
1765     def copy(self):
1766         return TypeArray.createByCopy(self._elementKind, self._arrayElementType, self._rank)
1767
1768
1769 # Corresponding to szarray type
1770 #
1771 # To create from data structures using references: TypeSZArray.createByRef(...)
1772 # To create from data structures using copy: TypeSZArray.createByCopy(...)
1773 #
1774 class TypeSZArray(IType):
1775
1776     # Empty constructor, do not use it directly
1777     # Create new objects by createByCopy or createByRef
1778     def __init__(self):
1779         self._elementKind = None
1780         self._arrayElementType = None
1781
1782     # Equality comparison operator
1783     def __eq__(self, rhs):
1784         if (rhs is None):
1785             return False
1786         if (not issubclass(type(rhs), TypeSZArray)):
1787             return False
1788         return self._elementKind == rhs._elementKind and self._arrayElementType == rhs._arrayElementType
1789
1790     # Convert to string
1791     def getStr(self, modules=None):
1792         if (not modules is None):
1793             if (not issubclass(type(modules), list)):
1794                 raise InternalError()
1795             for i in modules:
1796                 if (i is None or not issubclass(type(i), ModuleRecordExtended)):
1797                     raise InternalError()
1798
1799         return CorTypeInfo.getTypeMapEntry(self._elementKind).getName() + "{[" \
1800                + self._arrayElementType.getStr(modules) + "]}"
1801
1802     # Get elementKind
1803     def getElementKind(self):
1804         return self._elementKind
1805
1806     # Get arrayElementType
1807     def getArrayElementType(self):
1808         return self._arrayElementType
1809
1810     # Get all module indexes used in related types
1811     def getAllModuleIndexes(self):
1812         return self._arrayElementType.getAllModuleIndexes()
1813
1814     # Update all module indexes according to map old_index->new_index
1815     def updateModuleIndex(self, moduleIndexMap):
1816         if (moduleIndexMap is None or not issubclass(type(moduleIndexMap), list)):
1817             raise InternalError()
1818         self._arrayElementType.updateModuleIndex(moduleIndexMap)
1819         self.verify()
1820
1821     # Verify consistency
1822     def verify(self):
1823         if (self._elementKind is None or self._arrayElementType is None):
1824             raise InternalError()
1825
1826         if (not issubclass(type(self._elementKind), int) or not issubclass(type(self._arrayElementType), IType)):
1827             raise InternalError()
1828
1829         if (self._elementKind < 0 or self._elementKind >= CorTypeInfo.CorElementType.X_ELEMENT_TYPE_LAST):
1830             raise InternalError()
1831
1832     # Create from objects taking them by reference
1833     @staticmethod
1834     def createByRef(elementKind, arrayElementType):
1835         handle = TypeSZArray()
1836
1837         handle._elementKind = elementKind # pylint: disable=protected-access
1838         handle._arrayElementType = arrayElementType # pylint: disable=protected-access
1839
1840         handle.verify()
1841         return handle
1842
1843     # Create from objects taking them by copy
1844     @staticmethod
1845     def createByCopy(elementKind, arrayElementType):
1846         return TypeSZArray.createByRef(elementKind, arrayElementType.copy())
1847
1848     # Copy object
1849     def copy(self):
1850         return TypeSZArray.createByCopy(self._elementKind, self._arrayElementType)
1851
1852
1853 # Corresponding to generic type
1854 #
1855 # To create from data structures using references: TypeGeneric.createByRef(...)
1856 # To create from data structures using copy: TypeGeneric.createByCopy(...)
1857 #
1858 class TypeGeneric(IType):
1859
1860     # Empty constructor, do not use it directly
1861     # Create new objects by createByCopy or createByRef
1862     def __init__(self):
1863         self._elementKind = None
1864         self._genericBaseType = None
1865         self._instArgs = None
1866
1867     # Equality comparison operator
1868     def __eq__(self, rhs):
1869         if (rhs is None):
1870             return False
1871         if (not issubclass(type(rhs), TypeGeneric)):
1872             return False
1873         return self._elementKind == rhs._elementKind and self._genericBaseType == rhs._genericBaseType \
1874                and self._instArgs == rhs._instArgs
1875
1876     # Convert to string
1877     def getStr(self, modules=None):
1878         if (not modules is None):
1879             if (not issubclass(type(modules), list)):
1880                 raise InternalError()
1881             for i in modules:
1882                 if (i is None or not issubclass(type(i), ModuleRecordExtended)):
1883                     raise InternalError()
1884
1885         output = CorTypeInfo.getTypeMapEntry(self._elementKind).getName() + "{" \
1886                  + self._genericBaseType.getStr(modules) + "<"
1887
1888         for index in range(len(self._instArgs)):
1889             output += self._instArgs[index].getStr(modules)
1890             if (index != (len(self._instArgs) - 1)):
1891                 output += ","
1892
1893         output += ">}"
1894         return output
1895
1896     # Get elementKind
1897     def getElementKind(self):
1898         return self._elementKind
1899
1900     # Get genericBaseType
1901     def getGenericBaseType(self):
1902         return self._genericBaseType
1903
1904     # Get args
1905     def getInstArgs(self):
1906         return self._instArgs
1907
1908     # Get all module indexes used in related types
1909     def getAllModuleIndexes(self):
1910         indexes = self._genericBaseType.getAllModuleIndexes()
1911         for i in self._instArgs:
1912             indexes += i.getAllModuleIndexes()
1913         return sorted(set(indexes))
1914
1915     # Update all module indexes according to map old_index->new_index
1916     def updateModuleIndex(self, moduleIndexMap):
1917         if (moduleIndexMap is None or not issubclass(type(moduleIndexMap), list)):
1918             raise InternalError()
1919         self._genericBaseType.updateModuleIndex(moduleIndexMap)
1920         for i in self._instArgs:
1921             i.updateModuleIndex(moduleIndexMap)
1922         self.verify()
1923
1924     # Verify consistency
1925     def verify(self):
1926         if (self._elementKind is None or self._genericBaseType is None or self._instArgs is None):
1927             raise InternalError()
1928
1929         if (not issubclass(type(self._elementKind), int) or not issubclass(type(self._genericBaseType), TypeToken)
1930             or not issubclass(type(self._instArgs), list)):
1931             raise InternalError()
1932
1933         if (self._elementKind < 0 or self._elementKind >= CorTypeInfo.CorElementType.X_ELEMENT_TYPE_LAST):
1934             raise InternalError()
1935
1936         for i in self._instArgs:
1937             if (not issubclass(type(i), IType)):
1938                 raise InternalError()
1939
1940     # Create from objects taking them by reference
1941     @staticmethod
1942     def createByRef(elementKind, genericBaseType, instArgs):
1943         handle = TypeGeneric()
1944
1945         handle._elementKind = elementKind # pylint: disable=protected-access
1946         handle._genericBaseType = genericBaseType # pylint: disable=protected-access
1947         handle._instArgs = instArgs # pylint: disable=protected-access
1948
1949         handle.verify()
1950         return handle
1951
1952     # Create from objects taking them by copy
1953     @staticmethod
1954     def createByCopy(elementKind, genericBaseType, instArgs):
1955         copiedArgs = []
1956         for i in instArgs:
1957             copiedArgs.append(i.copy())
1958
1959         return TypeGeneric.createByRef(elementKind, genericBaseType.copy(), copiedArgs)
1960
1961     # Copy object
1962     def copy(self):
1963         return TypeGeneric.createByCopy(self._elementKind, self._genericBaseType, self._instArgs)
1964
1965
1966 # Corresponding to fnptr type
1967 #
1968 # To create from data structures using references: TypeFNPtr.createByRef(...)
1969 # To create from data structures using copy: TypeFNPtr.createByCopy(...)
1970 #
1971 class TypeFNPtr(IType):
1972
1973     # Empty constructor, do not use it directly
1974     # Create new objects by createByCopy or createByRef
1975     def __init__(self):
1976         self._elementKind = None
1977         self._callConv = None
1978         self._retAndArgs = None
1979
1980     # Equality comparison operator
1981     def __eq__(self, rhs):
1982         if (rhs is None):
1983             return False
1984         if (not issubclass(type(rhs), TypeFNPtr)):
1985             return False
1986         return self._elementKind == rhs._elementKind and self._callConv == rhs._callConv \
1987                and self._retAndArgs == rhs._retAndArgs
1988
1989     # Convert to string
1990     def getStr(self, modules=None):
1991         if (not modules is None):
1992             if (not issubclass(type(modules), list)):
1993                 raise InternalError()
1994             for i in modules:
1995                 if (i is None or not issubclass(type(i), ModuleRecordExtended)):
1996                     raise InternalError()
1997
1998         output = CorTypeInfo.getTypeMapEntry(self._elementKind).getName() + "{" + str(self._callConv) + "("
1999
2000         for index in range(len(self._retAndArgs)):
2001             output += self._retAndArgs[index].getStr(modules)
2002             if (index != (len(self._retAndArgs) - 1)):
2003                 output += ","
2004
2005         output += ")}"
2006         return output
2007
2008     # Get elementKind
2009     def getElementKind(self):
2010         return self._elementKind
2011
2012     # Get callConv
2013     def getCallConv(self):
2014         return self._callConv
2015
2016     # Get retAndArgs
2017     def getRetAndArgs(self):
2018         return self._retAndArgs
2019
2020     # Get all module indexes used in related types
2021     def getAllModuleIndexes(self):
2022         indexes = []
2023         for i in self._retAndArgs:
2024             indexes += i.getAllModuleIndexes()
2025         return sorted(set(indexes))
2026
2027     # Update all module indexes according to map old_index->new_index
2028     def updateModuleIndex(self, moduleIndexMap):
2029         if (moduleIndexMap is None or not issubclass(type(moduleIndexMap), list)):
2030             raise InternalError()
2031         for i in self._retAndArgs:
2032             i.updateModuleIndex(moduleIndexMap)
2033         self.verify()
2034
2035     # Verify consistency
2036     def verify(self):
2037         if (self._elementKind is None or self._callConv is None or self._retAndArgs is None):
2038             raise InternalError()
2039
2040         if (not issubclass(type(self._elementKind), int) or not issubclass(type(self._callConv), int)
2041             or not issubclass(type(self._retAndArgs), list)):
2042             raise InternalError()
2043
2044         if (self._elementKind < 0 or self._elementKind >= CorTypeInfo.CorElementType.X_ELEMENT_TYPE_LAST):
2045             raise InternalError()
2046
2047         if (self._callConv < 0 or self._callConv > Utility.getMaxForNBytes(RuntimeTypeSizes.char)):
2048             raise InternalError()
2049
2050         for i in self._retAndArgs:
2051             if (not issubclass(type(i), IType)):
2052                 raise InternalError()
2053
2054     # Create from objects taking them by reference
2055     @staticmethod
2056     def createByRef(elementKind, callConv, retAndArgs):
2057         handle = TypeFNPtr()
2058
2059         handle._elementKind = elementKind # pylint: disable=protected-access
2060         handle._callConv = callConv # pylint: disable=protected-access
2061         handle._retAndArgs = retAndArgs # pylint: disable=protected-access
2062
2063         handle.verify()
2064         return handle
2065
2066     # Create from objects taking them by copy
2067     @staticmethod
2068     def createByCopy(elementKind, callConv, retAndArgs):
2069         copiedRetAndArgs = []
2070         for i in retAndArgs:
2071             copiedRetAndArgs.append(i.copy())
2072
2073         return TypeFNPtr.createByRef(elementKind, callConv, copiedRetAndArgs)
2074
2075     # Copy object
2076     def copy(self):
2077         return TypeFNPtr.createByCopy(self._elementKind, self._callConv, self._retAndArgs)
2078
2079
2080 # Corresponding to generic method
2081 #
2082 # To create from data structures using references: GenericMethod.createByRef(...)
2083 # To create from data structures using copy: GenericMethod.createByCopy(...)
2084 #
2085 class GenericMethod:
2086
2087     # Empty constructor, do not use it directly
2088     # Create new objects by createByCopy or createByRef
2089     def __init__(self):
2090         self._methodFlags = None
2091         self._moduleIndex = None
2092         self._typeHandle = None
2093         self._methodToken = None
2094         self._methodInstArgs = None
2095
2096     # Equality comparison operator
2097     def __eq__(self, rhs):
2098         if (rhs is None):
2099             return False
2100         if (not issubclass(type(rhs), GenericMethod)):
2101             return False
2102         return self._methodFlags == rhs._methodFlags and self._moduleIndex == rhs._moduleIndex \
2103                and self._typeHandle == rhs._typeHandle and self._methodToken == rhs._methodToken \
2104                and self._methodInstArgs == rhs._methodInstArgs
2105
2106     # Convert to string
2107     def getStr(self, modules=None):
2108         if (not modules is None):
2109             if (not issubclass(type(modules), list)):
2110                 raise InternalError()
2111             for i in modules:
2112                 if (i is None or not issubclass(type(i), ModuleRecordExtended)):
2113                     raise InternalError()
2114
2115         moduleStr = str(self._moduleIndex)
2116         if (not modules is None):
2117             moduleStr = modules[self._moduleIndex].getModuleName()
2118
2119         output = "method{token{" + str(self._methodToken) + ":" + moduleStr + "}"
2120
2121         if (not self._methodInstArgs is None):
2122             output += "<"
2123             for index in range(len(self._methodInstArgs)):
2124                 output += self._methodInstArgs[index].getStr(modules)
2125                 if (index != (len(self._methodInstArgs) - 1)):
2126                     output += ","
2127             output += ">"
2128
2129         output += "@type{" + self._typeHandle.getStr(modules) + "}}"
2130         return output
2131
2132     # Get methodFlags
2133     def getMethodFlags(self):
2134         return self._methodFlags
2135
2136     # Get moduleIndex
2137     def getModuleIndex(self):
2138         return self._moduleIndex
2139
2140     # Get typeHandle
2141     def getTypeHandle(self):
2142         return self._typeHandle
2143
2144     # Get methodToken
2145     def getMethodToken(self):
2146         return self._methodToken
2147
2148     # Get methodInstArgs
2149     def getMethodInstArgs(self):
2150         return self._methodInstArgs
2151
2152     # Get all module indexes used in related types
2153     def getAllModuleIndexes(self):
2154         indexes = [self._moduleIndex]
2155         indexes += self._typeHandle.getAllModuleIndexes()
2156         if (not self._methodInstArgs is None):
2157             for i in self._methodInstArgs:
2158                 indexes += i.getAllModuleIndexes()
2159         return sorted(set(indexes))
2160
2161     # Update all module indexes according to map old_index->new_index
2162     def updateModuleIndex(self, moduleIndexMap):
2163         if (moduleIndexMap is None or not issubclass(type(moduleIndexMap), list)):
2164             raise InternalError()
2165
2166         self._moduleIndex = moduleIndexMap[self._moduleIndex]
2167         self._typeHandle.updateModuleIndex(moduleIndexMap)
2168
2169         if (not self._methodInstArgs is None):
2170             for i in self._methodInstArgs:
2171                 i.updateModuleIndex(moduleIndexMap)
2172
2173         self.verify()
2174
2175     # Verify consistency
2176     def verify(self):
2177         if (self._methodFlags is None or self._moduleIndex is None or self._typeHandle is None
2178             or self._methodToken is None):
2179             raise InternalError()
2180
2181         if (not issubclass(type(self._methodFlags), int) or not issubclass(type(self._moduleIndex), int)
2182             or not issubclass(type(self._typeHandle), IType) or not issubclass(type(self._methodToken), int)):
2183             raise InternalError()
2184
2185         if (self._methodFlags < 0 or self._methodFlags > Utility.getMaxForNBytes(RuntimeTypeSizes.int)):
2186             raise InternalError()
2187
2188         if (self._moduleIndex < 0 or self._moduleIndex >= RuntimeConstants.MAX_MODULES):
2189             raise InternalError()
2190
2191         if (self._methodToken < 0 or self._methodToken > Utility.getMaxForNBytes(RuntimeTypeSizes.int)):
2192             raise InternalError()
2193
2194         if (not self._methodInstArgs is None):
2195             if (not issubclass(type(self._methodInstArgs), list)):
2196                 raise InternalError()
2197
2198             for i in self._methodInstArgs:
2199                 if (not issubclass(type(i), IType)):
2200                     raise InternalError()
2201
2202     # Create from objects taking them by reference
2203     @staticmethod
2204     def createByRef(methodFlags, moduleIndex, typeHandle, methodToken, methodInstArgs):
2205         method = GenericMethod()
2206
2207         method._methodFlags = methodFlags # pylint: disable=protected-access
2208         method._moduleIndex = moduleIndex # pylint: disable=protected-access
2209         method._typeHandle = typeHandle # pylint: disable=protected-access
2210         method._methodToken = methodToken # pylint: disable=protected-access
2211         method._methodInstArgs = methodInstArgs # pylint: disable=protected-access
2212
2213         method.verify()
2214         return method
2215
2216     # Create from objects taking them by copy
2217     @staticmethod
2218     def createByCopy(methodFlags, moduleIndex, typeHandle, methodToken, methodInstArgs):
2219         copiedInstArgs = None
2220         if (not methodInstArgs is None):
2221             copiedInstArgs = []
2222             for i in methodInstArgs:
2223                 copiedInstArgs.append(i.copy())
2224
2225         return GenericMethod.createByRef(methodFlags, moduleIndex, typeHandle.copy(), methodToken, copiedInstArgs)
2226
2227     # Copy object
2228     def copy(self):
2229         return GenericMethod.createByCopy(self._methodFlags, self._moduleIndex, self._typeHandle,
2230                                           self._methodToken, self._methodInstArgs)
2231
2232 #----------------------------------------------------------------------------------------------------------------------
2233 # Utilities for binary signature encoding/decoding
2234 class SignatureDecoderUtil:
2235
2236     # Corresponds to EncodeMethodSigFlags
2237     # TODO: verify these constants somehow, see above
2238     class EncodeMethodSigFlags: # pylint: disable=too-few-public-methods
2239         ENCODE_METHOD_SIG_UnboxingStub              = 0x01
2240         ENCODE_METHOD_SIG_InstantiatingStub         = 0x02
2241         ENCODE_METHOD_SIG_MethodInstantiation       = 0x04
2242         ENCODE_METHOD_SIG_SlotInsteadOfToken        = 0x08
2243         ENCODE_METHOD_SIG_MemberRefToken            = 0x10
2244         ENCODE_METHOD_SIG_Constrained               = 0x20
2245         ENCODE_METHOD_SIG_OwnerType                 = 0x40
2246         ENCODE_METHOD_SIG_UpdateContext             = 0x80
2247
2248     # Corresponds to CorTokenType
2249     # TODO: verify these constants somehow, see above
2250     class CorTokenType: # pylint: disable=too-few-public-methods
2251         mdtModule               = 0x00000000
2252         mdtTypeRef              = 0x01000000
2253         mdtTypeDef              = 0x02000000
2254         mdtFieldDef             = 0x04000000
2255         mdtMethodDef            = 0x06000000
2256         mdtParamDef             = 0x08000000
2257         mdtInterfaceImpl        = 0x09000000
2258         mdtMemberRef            = 0x0a000000
2259         mdtCustomAttribute      = 0x0c000000
2260         mdtPermission           = 0x0e000000
2261         mdtSignature            = 0x11000000
2262         mdtEvent                = 0x14000000
2263         mdtProperty             = 0x17000000
2264         mdtMethodImpl           = 0x19000000
2265         mdtModuleRef            = 0x1a000000
2266         mdtTypeSpec             = 0x1b000000
2267         mdtAssembly             = 0x20000000
2268         mdtAssemblyRef          = 0x23000000
2269         mdtFile                 = 0x26000000
2270         mdtExportedType         = 0x27000000
2271         mdtManifestResource     = 0x28000000
2272         mdtNestedClass          = 0x29000000
2273         mdtGenericParam         = 0x2a000000
2274         mdtMethodSpec           = 0x2b000000
2275         mdtGenericParamConstraint = 0x2c000000
2276         mdtString               = 0x70000000
2277         mdtName                 = 0x71000000
2278         mdtBaseType             = 0x72000000
2279
2280     # Get token from rid and type
2281     @staticmethod
2282     def getTokenFromRid(rid, typ):
2283         return rid | typ
2284
2285     # Get rid from token
2286     @staticmethod
2287     def getRidFromToken(token):
2288         return token & 0x00ffffff
2289
2290     # Get type from token
2291     @staticmethod
2292     def getTypeFromToken(token):
2293         return token & 0xff000000
2294
2295 #----------------------------------------------------------------------------------------------------------------------
2296 # Decoder for binary signature of method that is created by MCJ
2297 #
2298 # Conceptually corresponds to ZapSig::DecodeMethod
2299 #
2300 class MethodBinarySignatureDecoder: # pylint: disable=too-few-public-methods
2301
2302     # Constructor with module index for binary signature and binary signature itself
2303     def __init__(self, moduleIndex, bytesArr):
2304         if (moduleIndex is None or bytesArr is None):
2305             raise InternalError()
2306         if (not issubclass(type(moduleIndex), int) or not issubclass(type(bytesArr), list)):
2307             raise InternalError()
2308
2309         for i in bytesArr:
2310             if (i < 0 or i > Utility.getMaxForNBytes(RuntimeTypeSizes.char)):
2311                 raise InternalError()
2312
2313         self._moduleIndex = moduleIndex
2314         self._bytesArr = bytesArr
2315         self._index = 0
2316
2317     # Decode one byte from binary signature
2318     #
2319     # See SigBuilder::AppendByte
2320     def _decodeByte(self):
2321         value = self._bytesArr[self._index]
2322         self._index += 1
2323         return value
2324
2325     # Decode value (1, 2 or 4 bytes) from binary signature
2326     #
2327     # See SigBuilder::AppendData
2328     def _decodeValue(self):
2329         value = 0
2330
2331         if ((self._bytesArr[self._index] & 0x80) == 0):
2332             # 1 byte case
2333             length = 1
2334             if (self._index + length > len(self._bytesArr)):
2335                 raise ProfileInconsistencyError()
2336             value = self._bytesArr[self._index]
2337             if (value > 0x7f):
2338                 raise InternalError()
2339         elif ((self._bytesArr[self._index] & 0xc0) == 0x80):
2340             # 2 byte case
2341             length = 2
2342             if (self._index + length > len(self._bytesArr)):
2343                 raise ProfileInconsistencyError()
2344             value = ((self._bytesArr[self._index] & 0x3f) << 8) | self._bytesArr[self._index + 1]
2345             if (value > 0x3fff):
2346                 raise InternalError()
2347         elif ((self._bytesArr[self._index] & 0xe0) == 0xc0):
2348             # 4 byte case
2349             length = 4
2350             if (self._index + length > len(self._bytesArr)):
2351                 raise ProfileInconsistencyError()
2352             value = ((self._bytesArr[self._index] & 0x1f) << 24) | (self._bytesArr[self._index + 1] << 16)
2353             value = value | (self._bytesArr[self._index + 2] << 8) | self._bytesArr[self._index + 3]
2354             if (value > 0x1fffffff):
2355                 raise InternalError()
2356         else:
2357             raise ProfileInconsistencyError()
2358
2359         self._index += length
2360
2361         return value
2362
2363     # Decode token from binary signature
2364     #
2365     # See SigBuilder::AppendToken
2366     def _decodeToken(self):
2367         value = self._decodeValue()
2368
2369         # get encode type and rid
2370         encodedType = value & 0x3
2371         rid = value >> 2
2372         typ = None
2373
2374         if (encodedType == 0x0):
2375             typ = SignatureDecoderUtil.CorTokenType.mdtTypeDef
2376         elif (encodedType == 0x1):
2377             typ = SignatureDecoderUtil.CorTokenType.mdtTypeRef
2378         elif (encodedType == 0x2):
2379             typ = SignatureDecoderUtil.CorTokenType.mdtTypeSpec
2380         elif (encodedType == 0x3):
2381             typ = SignatureDecoderUtil.CorTokenType.mdtBaseType
2382         else:
2383             raise ProfileInconsistencyError()
2384
2385         return SignatureDecoderUtil.getTokenFromRid(rid, typ)
2386
2387     # Decode type from binary signature
2388     #
2389     # See ZapSig::GetSignatureForTypeHandle
2390     def _decodeSignatureForTypeHandle(self, moduleIndex): # pylint: disable=too-many-locals, too-many-return-statements, too-many-statements
2391         if (moduleIndex is None or not issubclass(type(moduleIndex), int)):
2392             raise InternalError()
2393
2394         # I. Decode type
2395         typ = self._decodeByte()
2396
2397         # II. Decode primitive type
2398         if (typ < CorTypeInfo.CorElementType.ELEMENT_TYPE_MAX
2399             and (CorTypeInfo.getTypeMapEntry(typ).getIsPrim()
2400                  or typ == CorTypeInfo.CorElementType.ELEMENT_TYPE_STRING
2401                  or typ == CorTypeInfo.CorElementType.ELEMENT_TYPE_OBJECT)):
2402             return TypeSimple.createByRef(typ)
2403
2404         # III. Decode non-primitive type
2405         if (typ == CorTypeInfo.CorElementType.ELEMENT_TYPE_TYPEDBYREF): # pylint: disable=no-else-raise
2406             # TODO: support this?
2407             raise UnsupportedError()
2408         elif (typ == CorTypeInfo.CorElementType.ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG):
2409             paramType = self._decodeSignatureForTypeHandle(moduleIndex)
2410             return TypeParamType.createByRef(typ, paramType)
2411         elif (typ == CorTypeInfo.CorElementType.ELEMENT_TYPE_CANON_ZAPSIG):
2412             return TypeSimple.createByRef(typ)
2413         elif (typ == CorTypeInfo.CorElementType.ELEMENT_TYPE_MODULE_ZAPSIG):
2414             nextModuleIndex = self._decodeValue()
2415             return self._decodeSignatureForTypeHandle(nextModuleIndex)
2416         elif (typ == CorTypeInfo.CorElementType.ELEMENT_TYPE_VAR_ZAPSIG):
2417             rid = self._decodeValue()
2418             typeToken = SignatureDecoderUtil.getTokenFromRid(rid, SignatureDecoderUtil.CorTokenType.mdtGenericParam)
2419             return TypeToken.createByRef(typ, moduleIndex, typeToken)
2420         elif (typ == CorTypeInfo.CorElementType.ELEMENT_TYPE_VAR): # pylint: disable=no-else-raise
2421             # TODO: support this?
2422             raise UnsupportedError()
2423         elif (typ == CorTypeInfo.CorElementType.ELEMENT_TYPE_MVAR): # pylint: disable=no-else-raise
2424             # TODO: support this?
2425             raise UnsupportedError()
2426         elif (typ == CorTypeInfo.CorElementType.ELEMENT_TYPE_GENERICINST):
2427             nextTyp = self._decodeByte()
2428
2429             if (nextTyp == CorTypeInfo.CorElementType.ELEMENT_TYPE_INTERNAL): # pylint: disable=no-else-raise
2430                 # TODO: support this?
2431                 raise UnsupportedError()
2432             else:
2433                 typeToken = self._decodeToken()
2434                 tid = SignatureDecoderUtil.getTypeFromToken(typeToken)
2435
2436                 if (not tid in (SignatureDecoderUtil.CorTokenType.mdtTypeRef,
2437                                 SignatureDecoderUtil.CorTokenType.mdtTypeDef)):
2438                     raise ProfileInconsistencyError()
2439
2440                 numArgs = self._decodeValue()
2441                 args = []
2442                 for i in range(numArgs): # pylint: disable=unused-variable
2443                     args.append(self._decodeSignatureForTypeHandle(moduleIndex))
2444
2445                 return TypeGeneric.createByRef(typ, TypeToken.createByRef(nextTyp, moduleIndex, typeToken), args)
2446         elif (typ in (CorTypeInfo.CorElementType.ELEMENT_TYPE_CLASS,
2447                       CorTypeInfo.CorElementType.ELEMENT_TYPE_VALUETYPE)):
2448             typeToken = self._decodeToken()
2449             tid = SignatureDecoderUtil.getTypeFromToken(typeToken)
2450
2451             if (not tid in (SignatureDecoderUtil.CorTokenType.mdtTypeRef,
2452                             SignatureDecoderUtil.CorTokenType.mdtTypeDef)):
2453                 raise ProfileInconsistencyError()
2454
2455             return TypeToken.createByRef(typ, moduleIndex, typeToken)
2456         elif (typ in (CorTypeInfo.CorElementType.ELEMENT_TYPE_ARRAY, CorTypeInfo.CorElementType.ELEMENT_TYPE_SZARRAY)):
2457             elemType = self._decodeSignatureForTypeHandle(moduleIndex)
2458
2459             if (typ == CorTypeInfo.CorElementType.ELEMENT_TYPE_ARRAY): # pylint: disable=no-else-return
2460                 rank = self._decodeValue()
2461                 # Next two values are always written as 0 (see ZapSig::GetSignatureForTypeHandle)
2462                 nsizes = self._decodeValue()
2463                 nlbounds = self._decodeValue()
2464
2465                 if (nsizes != 0 or nlbounds != 0):
2466                     raise UnsupportedError()
2467
2468                 return TypeArray.createByRef(typ, elemType, rank)
2469             else:
2470                 return TypeSZArray.createByRef(typ, elemType)
2471         elif (typ == CorTypeInfo.CorElementType.ELEMENT_TYPE_PINNED):
2472             nextType = self._decodeSignatureForTypeHandle(moduleIndex)
2473             return TypeParamType.createByRef(typ, nextType)
2474         elif (typ in (CorTypeInfo.CorElementType.ELEMENT_TYPE_BYREF, CorTypeInfo.CorElementType.ELEMENT_TYPE_PTR)):
2475             paramType = self._decodeSignatureForTypeHandle(moduleIndex)
2476             return TypeParamType.createByRef(typ, paramType)
2477         elif (typ == CorTypeInfo.CorElementType.ELEMENT_TYPE_FNPTR):
2478             callConv = self._decodeByte()
2479             cArgs = self._decodeValue()
2480             retAndArgs = []
2481             for i in range(cArgs):
2482                 retAndArgs.append(self._decodeSignatureForTypeHandle(moduleIndex))
2483             return TypeFNPtr.createByRef(typ, callConv, retAndArgs)
2484         elif (typ == CorTypeInfo.CorElementType.ELEMENT_TYPE_INTERNAL): # pylint: disable=no-else-raise
2485             # TODO: support this?
2486             raise UnsupportedError()
2487         elif (typ == CorTypeInfo.CorElementType.ELEMENT_TYPE_SENTINEL): # pylint: disable=no-else-raise
2488             # TODO: support this?
2489             raise UnsupportedError()
2490         else:
2491             raise InternalError()
2492
2493     # Decode binary signature for method, result can be used by reference
2494     #
2495     # See ZapSig::DecodeMethod
2496     def decodeMethod(self):
2497         typeHandle = None
2498         methodToken = None
2499         numInstArgs = None
2500         methodInstArgs = None
2501
2502         # I. Decode method flags
2503         methodFlags = self._decodeValue()
2504
2505         # II. Decode type
2506         if (methodFlags & SignatureDecoderUtil.EncodeMethodSigFlags.ENCODE_METHOD_SIG_UpdateContext != 0):
2507             # Should not be encoded in mcj profile
2508             raise UnsupportedError()
2509
2510         if ((methodFlags & SignatureDecoderUtil.EncodeMethodSigFlags.ENCODE_METHOD_SIG_OwnerType) != 0):
2511             typeHandle = self._decodeSignatureForTypeHandle(self._moduleIndex)
2512         else:
2513             raise UnsupportedError()
2514
2515         # III. Decode method token
2516         if (methodFlags & SignatureDecoderUtil.EncodeMethodSigFlags.ENCODE_METHOD_SIG_SlotInsteadOfToken != 0): # pylint: disable=no-else-raise
2517             raise UnsupportedError()
2518         else:
2519             rid = self._decodeValue()
2520
2521             if (methodFlags & SignatureDecoderUtil.EncodeMethodSigFlags.ENCODE_METHOD_SIG_MemberRefToken):
2522                 methodToken = SignatureDecoderUtil.getTokenFromRid(rid, SignatureDecoderUtil.CorTokenType.mdtMemberRef)
2523             else:
2524                 methodToken = SignatureDecoderUtil.getTokenFromRid(rid, SignatureDecoderUtil.CorTokenType.mdtMethodDef)
2525
2526         # IV. Decode method instantiation args
2527         if (methodFlags & SignatureDecoderUtil.EncodeMethodSigFlags.ENCODE_METHOD_SIG_MethodInstantiation != 0):
2528             numInstArgs = self._decodeValue()
2529
2530             methodInstArgs = []
2531             for i in range(numInstArgs): # pylint: disable=unused-variable
2532                 methodInstArgs.append(self._decodeSignatureForTypeHandle(self._moduleIndex))
2533
2534         if (methodFlags & SignatureDecoderUtil.EncodeMethodSigFlags.ENCODE_METHOD_SIG_Constrained != 0):
2535             raise UnsupportedError()
2536
2537         ret = GenericMethod.createByRef(methodFlags, self._moduleIndex, typeHandle, methodToken, methodInstArgs)
2538
2539         self._moduleIndex = None
2540         self._bytesArr = None
2541         self._index = None
2542
2543         return ret
2544
2545 #----------------------------------------------------------------------------------------------------------------------
2546 # Encoder for binary signature of method that is stored in MCJ profile
2547 #
2548 # Conceptually corresponds to ZapSig::EncodeMethod
2549 #
2550 class MethodBinarySignatureEncoder: # pylint: disable=too-few-public-methods
2551
2552     # Constructor with generic method handle
2553     def __init__(self, methodHandle):
2554         if (methodHandle is None or not issubclass(type(methodHandle), GenericMethod)):
2555             raise InternalError()
2556
2557         self._methodHandle = methodHandle
2558         self._bytesArr = []
2559
2560     # Encode one byte to binary signature
2561     #
2562     # See SigBuilder::AppendByte
2563     def _encodeByte(self, value):
2564         if (value is None or not issubclass(type(value), int)):
2565             raise InternalError()
2566         if (value < 0 or value > Utility.getMaxForNBytes(RuntimeTypeSizes.char)):
2567             raise InternalError()
2568
2569         self._bytesArr.append(value)
2570
2571     # Encode value (1, 2 or 4 bytes) to binary signature
2572     #
2573     # See SigBuilder::AppendData
2574     def _encodeValue(self, value):
2575         if (value is None or not issubclass(type(value), int)):
2576             raise InternalError()
2577         if (value < 0 or value > Utility.getMaxForNBytes(RuntimeTypeSizes.int)):
2578             raise InternalError()
2579
2580         if (value <= 0x7f):
2581             self._bytesArr.append(value)
2582         elif (value <= 0x3fff):
2583             self._bytesArr.append((value >> 8) | 0x80)
2584             self._bytesArr.append(value & 0xff)
2585         elif (value <= 0x1fffffff):
2586             self._bytesArr.append((value >> 24) | 0xc0)
2587             self._bytesArr.append((value >> 16) & 0xff)
2588             self._bytesArr.append((value >> 8) & 0xff)
2589             self._bytesArr.append(value & 0xff)
2590         else:
2591             raise InternalError()
2592
2593     # Encode token to binary signature
2594     #
2595     # See SigBuilder::AppendToken
2596     def _encodeToken(self, value):
2597         if (value is None or not issubclass(type(value), int)):
2598             raise InternalError()
2599         if (value < 0 or value > Utility.getMaxForNBytes(RuntimeTypeSizes.int)):
2600             raise InternalError()
2601
2602         rid = SignatureDecoderUtil.getRidFromToken(value)
2603         typ = SignatureDecoderUtil.getTypeFromToken(value)
2604
2605         if (rid > 0x3ffffff):
2606             raise InternalError()
2607
2608         rid = rid << 2
2609
2610         if (typ == SignatureDecoderUtil.CorTokenType.mdtTypeDef):
2611             pass
2612         elif (typ == SignatureDecoderUtil.CorTokenType.mdtTypeRef):
2613             rid |= 0x1
2614         elif (typ == SignatureDecoderUtil.CorTokenType.mdtTypeSpec):
2615             rid |= 0x2
2616         elif (typ == SignatureDecoderUtil.CorTokenType.mdtBaseType):
2617             rid |= 0x3
2618
2619         self._encodeValue(rid)
2620
2621     # Encode type to binary signature
2622     #
2623     # See ZapSig::GetSignatureForTypeHandle
2624     def _encodeSignatureForTypeHandle(self, typeHandle):
2625         if (typeHandle is None or not issubclass(type(typeHandle), IType)):
2626             raise InternalError()
2627
2628         # I. Encode element type
2629         if (issubclass(type(typeHandle), TypeSimple)):
2630             self._encodeByte(typeHandle.getElementKind())
2631         elif (issubclass(type(typeHandle), TypeToken)):
2632             if (typeHandle.getModuleIndex() != self._methodHandle.getModuleIndex()):
2633                 self._encodeByte(CorTypeInfo.CorElementType.ELEMENT_TYPE_MODULE_ZAPSIG)
2634                 self._encodeValue(typeHandle.getModuleIndex())
2635
2636             self._encodeByte(typeHandle.getElementKind())
2637
2638             if (typeHandle.getElementKind() == CorTypeInfo.CorElementType.ELEMENT_TYPE_VAR_ZAPSIG):
2639                 self._encodeValue(SignatureDecoderUtil.getRidFromToken(typeHandle.getTypeToken()))
2640             else:
2641                 self._encodeToken(typeHandle.getTypeToken())
2642         elif (issubclass(type(typeHandle), TypeParamType)):
2643             self._encodeByte(typeHandle.getElementKind())
2644             self._encodeSignatureForTypeHandle(typeHandle.getParamType())
2645         elif (issubclass(type(typeHandle), TypeArray)):
2646             self._encodeByte(typeHandle.getElementKind())
2647             self._encodeSignatureForTypeHandle(typeHandle.getArrayElementType())
2648             self._encodeValue(typeHandle.getRank())
2649             self._encodeValue(0)
2650             self._encodeValue(0)
2651         elif (issubclass(type(typeHandle), TypeSZArray)):
2652             self._encodeByte(typeHandle.getElementKind())
2653             self._encodeSignatureForTypeHandle(typeHandle.getArrayElementType())
2654         elif (issubclass(type(typeHandle), TypeGeneric)):
2655             genericBaseType = typeHandle.getGenericBaseType()
2656             if (genericBaseType.getModuleIndex() != self._methodHandle.getModuleIndex()):
2657                 self._encodeByte(CorTypeInfo.CorElementType.ELEMENT_TYPE_MODULE_ZAPSIG)
2658                 self._encodeValue(genericBaseType.getModuleIndex())
2659
2660             self._encodeByte(typeHandle.getElementKind())
2661             self._encodeByte(genericBaseType.getElementKind())
2662             self._encodeToken(genericBaseType.getTypeToken())
2663
2664             self._encodeValue(len(typeHandle.getInstArgs()))
2665             for i in typeHandle.getInstArgs():
2666                 self._encodeSignatureForTypeHandle(i)
2667         elif (issubclass(type(typeHandle), TypeFNPtr)):
2668             self._encodeByte(typeHandle.getElementKind())
2669             self._encodeByte(typeHandle.getCallConv())
2670
2671             self._encodeValue(len(typeHandle.getRetAndArgs()))
2672             for i in typeHandle.getRetAndArgs():
2673                 self._encodeSignatureForTypeHandle(i)
2674
2675     # Encode binary signature for method, result can be used by reference
2676     #
2677     # See ZapSig::EncodeMethod
2678     def encodeMethod(self):
2679         methodFlags = self._methodHandle.getMethodFlags()
2680
2681         # I. Encode method flags
2682         self._encodeValue(methodFlags)
2683
2684         # II. Encode type
2685         if ((methodFlags & SignatureDecoderUtil.EncodeMethodSigFlags.ENCODE_METHOD_SIG_OwnerType) != 0):
2686             self._encodeSignatureForTypeHandle(self._methodHandle.getTypeHandle())
2687         else:
2688             raise UnsupportedError()
2689
2690         # III. Encode method token
2691         if (methodFlags & SignatureDecoderUtil.EncodeMethodSigFlags.ENCODE_METHOD_SIG_SlotInsteadOfToken != 0): # pylint: disable=no-else-raise
2692             raise UnsupportedError()
2693         else:
2694             self._encodeValue(SignatureDecoderUtil.getRidFromToken(self._methodHandle.getMethodToken()))
2695
2696         # IV. Encode method instantiation args
2697         if (methodFlags & SignatureDecoderUtil.EncodeMethodSigFlags.ENCODE_METHOD_SIG_MethodInstantiation != 0):
2698             methodInstArgs = self._methodHandle.getMethodInstArgs()
2699
2700             if (methodInstArgs is None):
2701                 raise InternalError()
2702
2703             self._encodeValue(len(methodInstArgs))
2704             for i in methodInstArgs:
2705                 self._encodeSignatureForTypeHandle(i)
2706
2707         ret = self._bytesArr
2708
2709         self._bytesArr = None
2710         self._methodHandle = None
2711
2712         return ret
2713
2714 # ----------------------------------------------------------------------------------------------------------------------
2715 # Base class for records with module dependency or method
2716 #
2717 class Info(ABC):
2718
2719     # Get record type
2720     @abstractmethod
2721     def getRecordType(self):
2722         pass
2723
2724     # Get module index
2725     @abstractmethod
2726     def getModuleIndex(self):
2727         pass
2728
2729     # Get all module indexes used in related types
2730     def getAllModuleIndexes(self):
2731         pass
2732
2733     # Check if info is related to generic method
2734     @abstractmethod
2735     def isGenericMethodInfo(self):
2736         pass
2737
2738     # Check if info is related to non-generic method
2739     @abstractmethod
2740     def isNonGenericMethodInfo(self):
2741         pass
2742
2743     # Check if info is related to method
2744     @abstractmethod
2745     def isMethodInfo(self):
2746         pass
2747
2748     # Check if info is related to module
2749     @abstractmethod
2750     def isModuleInfo(self):
2751         pass
2752
2753 # ----------------------------------------------------------------------------------------------------------------------
2754 # Record with module dependency
2755 #
2756 # Conceptually corresponds to "struct RecorderInfo" from multicorejitimpl.h, but has slight differences
2757 #
2758 # To create from bytes: InfoModuleDependency.createFromBytes(bytes)
2759 # To create from data structures using references: InfoModuleDependency.createByRef(...)
2760 # To create from data structures using copy: InfoModuleDependency.createByCopy(...)
2761 #
2762 class InfoModuleDependency(Info):
2763
2764     # Empty constructor, do not use it directly
2765     # Create new objects by createByCopy or createByRef
2766     def __init__(self):
2767         self._moduleIndex = None
2768         self._moduleLoadLevel = None
2769
2770     # Equality comparison operator
2771     def __eq__(self, rhs):
2772         if (rhs is None or not issubclass(type(rhs), InfoModuleDependency)):
2773             return False
2774         return self._moduleIndex == rhs._moduleIndex and self._moduleLoadLevel == rhs._moduleLoadLevel
2775
2776     # Get record type
2777     def getRecordType(self): # pylint: disable=no-self-use
2778         return RuntimeConstants.MULTICOREJIT_MODULEDEPENDENCY_RECORD_ID
2779
2780     # Check if info is related to generic method
2781     def isGenericMethodInfo(self): # pylint: disable=no-self-use
2782         return False
2783
2784     # Check if info is related to non-generic method
2785     def isNonGenericMethodInfo(self): # pylint: disable=no-self-use
2786         return False
2787
2788     # Check if info is related to method
2789     def isMethodInfo(self): # pylint: disable=no-self-use
2790         return False
2791
2792     # Check if info is related to module
2793     def isModuleInfo(self): # pylint: disable=no-self-use
2794         return True
2795
2796     # Get module index
2797     def getModuleIndex(self):
2798         return self._moduleIndex
2799
2800     # Get all module indexes used in related types
2801     def getAllModuleIndexes(self):
2802         return [self._moduleIndex]
2803
2804     # Get module load level
2805     def getModuleLoadLevel(self):
2806         return self._moduleLoadLevel
2807
2808     # Update module index according to map old_index->new_index
2809     def updateModuleIndex(self, moduleIndexMap):
2810         if (moduleIndexMap is None or not issubclass(type(moduleIndexMap), list)):
2811             raise InternalError()
2812         self._moduleIndex = moduleIndexMap[self._moduleIndex]
2813         self.verify()
2814
2815     # Verify consistency
2816     def verify(self):
2817         if (self._moduleIndex is None or self._moduleLoadLevel is None):
2818             raise InternalError()
2819
2820         if (not issubclass(type(self._moduleIndex), int) or not issubclass(type(self._moduleLoadLevel), int)):
2821             raise InternalError()
2822
2823         if (self._moduleIndex < 0 or self._moduleIndex >= RuntimeConstants.MAX_MODULES):
2824             raise InternalError()
2825
2826         if (self._moduleLoadLevel < 0 or self._moduleLoadLevel >= RuntimeConstants.MAX_MODULE_LEVELS):
2827             raise InternalError()
2828
2829     # Encode info
2830     def _encodeInfo(self):
2831         data1 = 0
2832         # high byte is record id
2833         data1 |= RuntimeConstants.MULTICOREJIT_MODULEDEPENDENCY_RECORD_ID << RuntimeConstants.RECORD_TYPE_OFFSET
2834         # next byte is load level
2835         data1 |= self._moduleLoadLevel << RuntimeConstants.MODULE_LEVEL_OFFSET
2836         # two low bytes are module index
2837         data1 |= self._moduleIndex
2838         return data1
2839
2840     # Decode type of this record, module index and module load level
2841     @staticmethod
2842     def _decodeInfo(data1):
2843         recordType = data1 >> RuntimeConstants.RECORD_TYPE_OFFSET
2844         loadLevel = (data1 >> RuntimeConstants.MODULE_LEVEL_OFFSET) & (RuntimeConstants.MAX_MODULE_LEVELS - 1)
2845         moduleIndex = data1 & RuntimeConstants.MODULE_MASK
2846         return (recordType, loadLevel, moduleIndex)
2847
2848     # Print raw
2849     def printRaw(self, offsetStr=""):
2850         if (offsetStr is None or not issubclass(type(offsetStr), str)):
2851             raise InternalError()
2852
2853         data1 = self._encodeInfo()
2854
2855         print(offsetStr + ">>> Raw ModuleDependency:")
2856         print(offsetStr + "data1 = " + str(data1))
2857
2858     # Print pretty
2859     def print(self, offsetStr="", modules=None):
2860         if (offsetStr is None or not issubclass(type(offsetStr), str)):
2861             raise InternalError()
2862
2863         # Get module name if it is available
2864         moduleName = ""
2865         if (not modules is None):
2866             moduleName = modules[self.getModuleIndex()].getModuleName()
2867
2868         print(offsetStr + ">>> Record, Module Dependency:")
2869         print(offsetStr + "")
2870         print(offsetStr + "Module index: " + str(self.getModuleIndex())
2871               + ((" (" + moduleName + ")") if moduleName != "" else ""))
2872         print(offsetStr + "Module load level: " + str(self.getModuleLoadLevel()))
2873         print(offsetStr + "")
2874
2875     # Create from bytes
2876     @staticmethod
2877     def createFromBytes(bytesArr):
2878         if (bytesArr is None):
2879             raise InternalError()
2880
2881         if (not issubclass(type(bytesArr), list)):
2882             raise InternalError()
2883
2884         if (len(bytesArr) != RuntimeTypeSizes.int):
2885             raise ProfileInconsistencyError()
2886
2887         index = 0
2888
2889         data1 = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.int])
2890         index += RuntimeTypeSizes.int
2891
2892         if (index != RuntimeTypeSizes.int):
2893             raise InternalError()
2894
2895         recordType, moduleLoadLevel, moduleIndex = InfoModuleDependency._decodeInfo(data1)
2896
2897         if (recordType != RuntimeConstants.MULTICOREJIT_MODULEDEPENDENCY_RECORD_ID):
2898             raise InternalError()
2899
2900         return InfoModuleDependency.createByRef(moduleIndex, moduleLoadLevel)
2901
2902     # Create from objects taking them by reference
2903     @staticmethod
2904     def createByRef(moduleIndex, moduleLoadLevel):
2905         moduleDependency = InfoModuleDependency()
2906
2907         moduleDependency._moduleIndex = moduleIndex # pylint: disable=protected-access
2908         moduleDependency._moduleLoadLevel = moduleLoadLevel # pylint: disable=protected-access
2909
2910         moduleDependency.verify()
2911         return moduleDependency
2912
2913     # Create from objects taking them by copy
2914     @staticmethod
2915     def createByCopy(moduleIndex, moduleLoadLevel):
2916         return InfoModuleDependency.createByRef(moduleIndex, moduleLoadLevel)
2917
2918     # Copy object
2919     def copy(self):
2920         return InfoModuleDependency.createByCopy(self._moduleIndex, self._moduleLoadLevel)
2921
2922     # Convert object to list of bytes
2923     def convertToBytes(self):
2924         index = 0
2925
2926         bytesArr = []
2927
2928         data1 = self._encodeInfo()
2929
2930         bytesArr += Utility.splitNBytes(data1, RuntimeTypeSizes.int)
2931         index += RuntimeTypeSizes.int
2932
2933         if (index != RuntimeTypeSizes.int):
2934             raise InternalError()
2935
2936         return bytesArr
2937
2938 # ----------------------------------------------------------------------------------------------------------------------
2939 # Record with non-generic method
2940 #
2941 # Conceptually corresponds to "struct RecorderInfo" from multicorejitimpl.h, but has slight differences
2942 #
2943 # To create from bytes: InfoNonGenericMethod.createFromBytes(bytes)
2944 # To create from data structures using references: InfoNonGenericMethod.createByRef(...)
2945 # To create from data structures using copy: InfoNonGenericMethod.createByCopy(...)
2946 #
2947 class InfoNonGenericMethod(Info):
2948
2949     # Empty constructor, do not use it directly
2950     # Create new objects by createByCopy or createByRef
2951     def __init__(self):
2952         self._moduleIndex = None
2953         self._methodFlags = None
2954         self._methodToken = None
2955
2956     # Equality comparison operator
2957     def __eq__(self, rhs):
2958         if (rhs is None or not issubclass(type(rhs), InfoNonGenericMethod)):
2959             return False
2960         return self._moduleIndex == rhs._moduleIndex and self._methodFlags == rhs._methodFlags \
2961                and self._methodToken == rhs._methodToken
2962
2963     # Get record type
2964     def getRecordType(self): # pylint: disable=no-self-use
2965         return RuntimeConstants.MULTICOREJIT_METHOD_RECORD_ID
2966
2967     # Check if info is related to generic method
2968     def isGenericMethodInfo(self): # pylint: disable=no-self-use
2969         return False
2970
2971     # Check if info is related to non-generic method
2972     def isNonGenericMethodInfo(self): # pylint: disable=no-self-use
2973         return True
2974
2975     # Check if info is related to method
2976     def isMethodInfo(self): # pylint: disable=no-self-use
2977         return True
2978
2979     # Check if info is related to module
2980     def isModuleInfo(self): # pylint: disable=no-self-use
2981         return False
2982
2983     # Get module index
2984     def getModuleIndex(self):
2985         return self._moduleIndex
2986
2987     # Get all module indexes used in related types
2988     def getAllModuleIndexes(self):
2989         return [self._moduleIndex]
2990
2991     # Get method flags
2992     def getMethodFlags(self):
2993         return self._methodFlags
2994
2995     # Get method token
2996     def getMethodToken(self):
2997         return self._methodToken
2998
2999     # Update module index according to map old_index->new_index
3000     def updateModuleIndex(self, moduleIndexMap):
3001         if (moduleIndexMap is None or not issubclass(type(moduleIndexMap), list)):
3002             raise InternalError()
3003         self._moduleIndex = moduleIndexMap[self._moduleIndex]
3004         self.verify()
3005
3006     # Verify consistency
3007     def verify(self):
3008         if (self._moduleIndex is None or self._methodFlags is None or self._methodToken is None):
3009             raise InternalError()
3010
3011         if (not issubclass(type(self._moduleIndex), int) or not issubclass(type(self._methodFlags), int)
3012             or not issubclass(type(self._methodToken), int)):
3013             raise InternalError()
3014
3015         if (self._moduleIndex < 0 or self._moduleIndex >= RuntimeConstants.MAX_MODULES):
3016             raise InternalError()
3017
3018         if (self._methodFlags < 0
3019             or ((self._methodFlags | RuntimeConstants.METHOD_FLAGS_MASK) ^ RuntimeConstants.METHOD_FLAGS_MASK) != 0):
3020             raise InternalError()
3021
3022         if (self._methodToken < 0 or self._methodToken > Utility.getMaxForNBytes(RuntimeTypeSizes.int)):
3023             raise InternalError()
3024
3025     # Encode info
3026     def _encodeInfo(self):
3027         data1 = 0
3028         # high byte is record id
3029         data1 |= RuntimeConstants.MULTICOREJIT_METHOD_RECORD_ID << RuntimeConstants.RECORD_TYPE_OFFSET
3030         # next byte is method flags
3031         data1 |= self._methodFlags
3032         # two low bytes are module index
3033         data1 |= self._moduleIndex
3034
3035         # this is simply token
3036         data2 = self._methodToken
3037
3038         return (data1, data2)
3039
3040     # Decode type of this record, module index and module load level
3041     @staticmethod
3042     def _decodeInfo(data1, data2):
3043         recordType = data1 >> RuntimeConstants.RECORD_TYPE_OFFSET
3044         methodFlags = data1 & RuntimeConstants.METHOD_FLAGS_MASK
3045         moduleIndex = data1 & RuntimeConstants.MODULE_MASK
3046         methodToken = data2
3047         return (recordType, methodFlags, moduleIndex, methodToken)
3048
3049     # Print raw
3050     def printRaw(self, offsetStr=""):
3051         if (offsetStr is None or not issubclass(type(offsetStr), str)):
3052             raise InternalError()
3053
3054         data1, data2 = self._encodeInfo()
3055
3056         print(offsetStr + ">>> Raw Non-Generic MethodRecord:")
3057         print(offsetStr + "data1 = " + str(data1))
3058         print(offsetStr + "data2 = " + str(data2))
3059
3060     # Print pretty
3061     def print(self, offsetStr="", modules=None):
3062         if (offsetStr is None or not issubclass(type(offsetStr), str)):
3063             raise InternalError()
3064
3065         # Get module name if it is available
3066         moduleName = ""
3067         if (not modules is None):
3068             moduleName = modules[self.getModuleIndex()].getModuleName()
3069
3070         flagsStr = "jitted by background thread"
3071         if (self._methodFlags & RuntimeConstants.JIT_BY_APP_THREAD_TAG != 0):
3072             flagsStr = "jitted by foreground (app) thread"
3073
3074         print(offsetStr + ">>> Record, Non-generic Method:")
3075         print(offsetStr + "")
3076         print(offsetStr + "Module index: " + str(self.getModuleIndex())
3077               + ((" (" + moduleName + ")") if moduleName != "" else ""))
3078         print(offsetStr + "Method flags: " + str(self.getMethodFlags()) + " (" + flagsStr + ")")
3079         print(offsetStr + "Method token: " + str(self.getMethodToken()))
3080         print(offsetStr + "")
3081
3082     # Create from bytes
3083     @staticmethod
3084     def createFromBytes(bytesArr):
3085         if (bytesArr is None):
3086             raise InternalError()
3087
3088         if (not issubclass(type(bytesArr), list)):
3089             raise InternalError()
3090
3091         if (len(bytesArr) != 2 * RuntimeTypeSizes.int):
3092             raise ProfileInconsistencyError()
3093
3094         index = 0
3095
3096         data1 = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.int])
3097         index += RuntimeTypeSizes.int
3098
3099         data2 = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.int])
3100         index += RuntimeTypeSizes.int
3101
3102         if (index != 2 * RuntimeTypeSizes.int):
3103             raise InternalError()
3104
3105         recordType, methodFlags, moduleIndex, methodToken = InfoNonGenericMethod._decodeInfo(data1, data2)
3106
3107         if (recordType != RuntimeConstants.MULTICOREJIT_METHOD_RECORD_ID):
3108             raise InternalError()
3109
3110         return InfoNonGenericMethod.createByRef(moduleIndex, methodFlags, methodToken)
3111
3112     # Create from objects taking them by reference
3113     @staticmethod
3114     def createByRef(moduleIndex, methodFlags, methodToken):
3115         method = InfoNonGenericMethod()
3116
3117         method._moduleIndex = moduleIndex # pylint: disable=protected-access
3118         method._methodFlags = methodFlags # pylint: disable=protected-access
3119         method._methodToken = methodToken # pylint: disable=protected-access
3120
3121         method.verify()
3122         return method
3123
3124     # Create from objects taking them by copy
3125     @staticmethod
3126     def createByCopy(moduleIndex, methodFlags, methodToken):
3127         return InfoNonGenericMethod.createByRef(moduleIndex, methodFlags, methodToken)
3128
3129     # Copy object
3130     def copy(self):
3131         return InfoNonGenericMethod.createByCopy(self._moduleIndex, self._methodFlags, self._methodToken)
3132
3133     # Convert object to list of bytes
3134     def convertToBytes(self):
3135         index = 0
3136
3137         bytesArr = []
3138
3139         data1, data2 = self._encodeInfo()
3140
3141         bytesArr += Utility.splitNBytes(data1, RuntimeTypeSizes.int)
3142         index += RuntimeTypeSizes.int
3143
3144         bytesArr += Utility.splitNBytes(data2, RuntimeTypeSizes.int)
3145         index += RuntimeTypeSizes.int
3146
3147         if (index != 2 * RuntimeTypeSizes.int):
3148             raise InternalError()
3149
3150         return bytesArr
3151 # ----------------------------------------------------------------------------------------------------------------------
3152 # Record with generic method
3153 #
3154 # Conceptually corresponds to "struct RecorderInfo" from multicorejitimpl.h, but has slight differences
3155 #
3156 # To create from bytes: InfoGenericMethod.createFromBytes(bytes)
3157 # To create from data structures using references: InfoGenericMethod.createByRef(...)
3158 # To create from data structures using copy: InfoGenericMethod.createByCopy(...)
3159 #
3160 class InfoGenericMethod(Info): # pylint: disable=too-many-public-methods
3161
3162     # Empty constructor, do not use it directly
3163     # Create new objects by createByCopy or createByRef
3164     def __init__(self):
3165         self._methodFlags = None
3166         self._methodHandle = None
3167
3168     # Equality comparison operator
3169     def __eq__(self, rhs):
3170         if (rhs is None or not issubclass(type(rhs), InfoGenericMethod)):
3171             return False
3172         return self._methodFlags == rhs._methodFlags and self._methodHandle == rhs._methodHandle
3173
3174     # Get record type
3175     def getRecordType(self): # pylint: disable=no-self-use
3176         return RuntimeConstants.MULTICOREJIT_GENERICMETHOD_RECORD_ID
3177
3178     # Check if info is related to generic method
3179     def isGenericMethodInfo(self): # pylint: disable=no-self-use
3180         return True
3181
3182     # Check if info is related to non-generic method
3183     def isNonGenericMethodInfo(self): # pylint: disable=no-self-use
3184         return False
3185
3186     # Check if info is related to method
3187     def isMethodInfo(self): # pylint: disable=no-self-use
3188         return True
3189
3190     # Check if info is related to module
3191     def isModuleInfo(self): # pylint: disable=no-self-use
3192         return False
3193
3194     # Get module index
3195     def getModuleIndex(self):
3196         return self._methodHandle.getModuleIndex()
3197
3198     # Get all module indexes used in related types
3199     def getAllModuleIndexes(self):
3200         return self._methodHandle.getAllModuleIndexes()
3201
3202     # Get method flags
3203     def getMethodFlags(self):
3204         return self._methodFlags
3205
3206     # Get method handle
3207     def getMethodHandle(self):
3208         return self._methodHandle
3209
3210     # Get method token
3211     def getMethodToken(self):
3212         return self._methodHandle.getMethodToken()
3213
3214     # Update module index according to map old_index->new_index
3215     def updateModuleIndex(self, moduleIndexMap):
3216         if (moduleIndexMap is None or not issubclass(type(moduleIndexMap), list)):
3217             raise InternalError()
3218         self._methodHandle.updateModuleIndex(moduleIndexMap)
3219         self.verify()
3220
3221     # Verify consistency
3222     def verify(self):
3223         if (self._methodFlags is None or self._methodHandle is None):
3224             raise InternalError()
3225
3226         if (not issubclass(type(self._methodFlags), int) or not issubclass(type(self._methodHandle), GenericMethod)):
3227             raise InternalError()
3228
3229         if (self._methodFlags < 0
3230             or ((self._methodFlags | RuntimeConstants.METHOD_FLAGS_MASK) ^ RuntimeConstants.METHOD_FLAGS_MASK) != 0):
3231             raise InternalError()
3232
3233     # Encode info
3234     def _encodeInfo(self):
3235         binarySignature = MethodBinarySignatureEncoder(self._methodHandle).encodeMethod()
3236
3237         data1 = 0
3238         # high byte is record id
3239         data1 |= RuntimeConstants.MULTICOREJIT_GENERICMETHOD_RECORD_ID << RuntimeConstants.RECORD_TYPE_OFFSET
3240         # next byte is method flags
3241         data1 |= self._methodFlags
3242         # two low bytes are module index
3243         data1 |= self._methodHandle.getModuleIndex()
3244
3245         # this is simply length of binary signature
3246         data2 = len(binarySignature)
3247
3248         # this is simply binary signature
3249         ptr = binarySignature
3250
3251         return (data1, data2, ptr)
3252
3253     # Decode type of this record, module index and module load level
3254     @staticmethod
3255     def _decodeInfo(data1, data2, ptr): # pylint: disable=unused-argument
3256         recordType = data1 >> RuntimeConstants.RECORD_TYPE_OFFSET
3257         methodFlags = data1 & RuntimeConstants.METHOD_FLAGS_MASK
3258         moduleIndex = data1 & RuntimeConstants.MODULE_MASK
3259
3260         method = MethodBinarySignatureDecoder(moduleIndex, ptr).decodeMethod()
3261
3262         return (recordType, methodFlags, method)
3263
3264     # Print raw
3265     def printRaw(self, offsetStr=""):
3266         if (offsetStr is None or not issubclass(type(offsetStr), str)):
3267             raise InternalError()
3268
3269         data1, data2, ptr = self._encodeInfo()
3270
3271         size = RuntimeTypeSizes.int + RuntimeTypeSizes.short + data2
3272         padding = Utility.alignUp(size, RuntimeTypeSizes.int) - size
3273
3274         print(offsetStr + ">>> Raw Generic MethodRecord:")
3275         print(offsetStr + "data1 = " + str(data1))
3276         print(offsetStr + "data2 = " + str(data2))
3277         print(offsetStr + "ptr = " + str(ptr))
3278         print(offsetStr + "alignment padding = " + str(padding) + " bytes")
3279
3280     # Print pretty
3281     def print(self, offsetStr="", modules=None):
3282         if (offsetStr is None or not issubclass(type(offsetStr), str)):
3283             raise InternalError()
3284
3285         # Get module name if it is available
3286         moduleName = ""
3287         if (not modules is None):
3288             if (modules is None or not issubclass(type(modules), list)):
3289                 raise InternalError()
3290             for i in modules:
3291                 if (i is None or not issubclass(type(i), ModuleRecordExtended)):
3292                     raise InternalError()
3293
3294             moduleName = modules[self.getModuleIndex()].getModuleName()
3295
3296         flagsStr = "jitted by background thread"
3297         if (self._methodFlags & RuntimeConstants.JIT_BY_APP_THREAD_TAG != 0):
3298             flagsStr = "jitted by foreground (app) thread"
3299
3300         print(offsetStr + ">>> Record, Generic Method:")
3301         print(offsetStr + "")
3302         print(offsetStr + "Module index: " + str(self.getModuleIndex())
3303               + ((" (" + moduleName + ")") if moduleName != "" else ""))
3304         print(offsetStr + "Method flags: " + str(self.getMethodFlags()) + " (" + flagsStr + ")")
3305         print(offsetStr + "Decoded binary signature: " + self.getMethodHandle().getStr(modules))
3306         print(offsetStr + "")
3307
3308     # Create from bytes
3309     @staticmethod
3310     def createFromBytes(bytesArr):
3311         if (bytesArr is None):
3312             raise InternalError()
3313
3314         if (not issubclass(type(bytesArr), list)):
3315             raise InternalError()
3316
3317         if (len(bytesArr) < (RuntimeTypeSizes.int + RuntimeTypeSizes.short)):
3318             raise ProfileInconsistencyError()
3319
3320         index = 0
3321
3322         data1 = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.int])
3323         index += RuntimeTypeSizes.int
3324
3325         data2 = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.short])
3326         index += RuntimeTypeSizes.short
3327
3328         if (len(bytesArr) != Utility.alignUp(RuntimeTypeSizes.int + RuntimeTypeSizes.short + data2,
3329                                              RuntimeTypeSizes.int)):
3330             raise ProfileInconsistencyError()
3331
3332         ptr = bytesArr[index:index+data2]
3333         index += data2
3334
3335         if (index != RuntimeTypeSizes.int + RuntimeTypeSizes.short + data2):
3336             raise InternalError()
3337
3338         recordType, methodFlags, methodHandle = InfoGenericMethod._decodeInfo(data1, data2, ptr)
3339
3340         if (recordType != RuntimeConstants.MULTICOREJIT_GENERICMETHOD_RECORD_ID):
3341             raise InternalError()
3342
3343         return InfoGenericMethod.createByRef(methodFlags, methodHandle)
3344
3345     # Create from objects taking them by reference
3346     @staticmethod
3347     def createByRef(methodFlags, methodHandle):
3348         method = InfoGenericMethod()
3349
3350         method._methodFlags = methodFlags # pylint: disable=protected-access
3351         method._methodHandle = methodHandle # pylint: disable=protected-access
3352
3353         method.verify()
3354         return method
3355
3356     # Create from objects taking them by copy
3357     @staticmethod
3358     def createByCopy(methodFlags, methodHandle):
3359         return InfoGenericMethod.createByRef(methodFlags, methodHandle.copy())
3360
3361     # Copy object
3362     def copy(self):
3363         return InfoGenericMethod.createByCopy(self._methodFlags, self._methodHandle)
3364
3365     # Convert object to list of bytes
3366     def convertToBytes(self):
3367         index = 0
3368
3369         bytesArr = []
3370
3371         data1, data2, ptr = self._encodeInfo()
3372
3373         bytesArr += Utility.splitNBytes(data1, RuntimeTypeSizes.int)
3374         index += RuntimeTypeSizes.int
3375
3376         bytesArr += Utility.splitNBytes(data2, RuntimeTypeSizes.short)
3377         index += RuntimeTypeSizes.short
3378
3379         bytesArr += ptr
3380         index += len(ptr)
3381
3382         padding = Utility.alignUp(index, RuntimeTypeSizes.int) - index
3383         bytesArr += [0] * padding
3384         index += padding
3385
3386         if (index != Utility.alignUp(RuntimeTypeSizes.int + RuntimeTypeSizes.short + data2,
3387                                      RuntimeTypeSizes.int)):
3388             raise InternalError()
3389
3390         return bytesArr
3391
3392 # ----------------------------------------------------------------------------------------------------------------------
3393 # MultiCoreJit profile
3394 #
3395 # _moduleOrMethodInfo contains records with module deps and methods, both generic and non-generic,
3396 # records' order is the same as in profile
3397 #
3398 # To create from bytes: MCJProfile.createFromBytes(bytes)
3399 # To create from data structures using references: MCJProfile.createByRef(...)
3400 # To create from data structures using copy: MCJProfile.createByCopy(...)
3401 #
3402 class MCJProfile: # pylint: disable=too-many-public-methods
3403
3404     # Empty constructor, do not use it directly
3405     # Create new objects by createByCopy or createByRef
3406     def __init__(self):
3407         self._header = None
3408         self._modules = None
3409         self._moduleOrMethodInfo = None
3410
3411     # Equality comparison operator
3412     def __eq__(self, rhs):
3413         if (rhs is None):
3414             return False
3415         if (not issubclass(type(rhs), MCJProfile)):
3416             return False
3417
3418         if (self._header != rhs._header
3419             or len(self._modules) != len(rhs._modules)
3420             or len(self._moduleOrMethodInfo) != len(rhs._moduleOrMethodInfo)):
3421             return False
3422
3423         for index, module in enumerate(self._modules):
3424             if (module != rhs._modules[index]):
3425                 return False
3426
3427         for index, info in enumerate(self._moduleOrMethodInfo):
3428             if (info != rhs._moduleOrMethodInfo[index]):
3429                 return False
3430
3431         return True
3432
3433     # Get header
3434     def getHeader(self):
3435         return self._header
3436
3437     # Get modules
3438     def getModules(self):
3439         return self._modules
3440
3441     # Get moduleOrMethodInfo
3442     def getModuleOrMethodInfo(self):
3443         return self._moduleOrMethodInfo
3444
3445     # Verify consistency
3446     def verify(self):
3447         if (self._header is None or self._modules is None or self._moduleOrMethodInfo is None):
3448             raise InternalError()
3449
3450         if (not issubclass(type(self._header), HeaderRecord)
3451             or not issubclass(type(self._modules), list) or not issubclass(type(self._moduleOrMethodInfo), list)):
3452             raise InternalError()
3453
3454         for i in self._modules:
3455             if (i is None):
3456                 raise InternalError()
3457             if (not issubclass(type(i), ModuleRecordExtended)):
3458                 raise InternalError()
3459
3460         for i in self._moduleOrMethodInfo:
3461             if (i is None):
3462                 raise InternalError()
3463             if (not issubclass(type(i), InfoModuleDependency)
3464                 and not issubclass(type(i), InfoNonGenericMethod)
3465                 and not issubclass(type(i), InfoGenericMethod)):
3466                 raise InternalError()
3467
3468         if (self._header.getModuleCount() != len(self._modules)):
3469             raise ProfileInconsistencyError()
3470
3471         if ((self._header.getMethodCount() + self._header.getModuleDepCount()) != len(self._moduleOrMethodInfo)):
3472             raise ProfileInconsistencyError()
3473
3474         for info in self._moduleOrMethodInfo:
3475             indexes = info.getAllModuleIndexes()
3476             for i in indexes:
3477                 if (i >= len(self._modules)):
3478                     raise InternalError()
3479
3480     # Print raw header
3481     def printRawHeader(self, offsetStr=""):
3482         if (offsetStr is None or not issubclass(type(offsetStr), str)):
3483             raise InternalError()
3484
3485         print(offsetStr + ">>> Raw MCJProfile:")
3486         print(offsetStr + "header = {")
3487         self._header.printRaw(offsetStr + "  ")
3488         print(offsetStr + "}")
3489
3490     # Print raw modules
3491     def printRawModules(self, offsetStr=""):
3492         if (offsetStr is None or not issubclass(type(offsetStr), str)):
3493             raise InternalError()
3494
3495         print(offsetStr + "modules = [")
3496         for i in self._modules:
3497             print(offsetStr + "  " + "{")
3498             i.printRaw(offsetStr + "    ")
3499             print(offsetStr + "  " + "},")
3500         print(offsetStr + "]")
3501
3502     # Print raw module dependencies and methods
3503     def printRawModuleOrMethodInfo(self, offsetStr="", printModuleDeps=True,
3504                                    printGenericMethods=True, printNonGenericMethods=True):
3505         if (offsetStr is None or not issubclass(type(offsetStr), str)):
3506             raise InternalError()
3507
3508         if (printModuleDeps is None or printGenericMethods is None or printNonGenericMethods is None):
3509             raise InternalError()
3510
3511         if (not issubclass(type(printModuleDeps), bool) or not issubclass(type(printGenericMethods), bool)
3512             or not issubclass(type(printNonGenericMethods), bool)):
3513             raise InternalError()
3514
3515         name = ""
3516         if (printModuleDeps and printGenericMethods and printNonGenericMethods):
3517             name = "moduleOrMethodInfo"
3518         else:
3519             if (printModuleDeps):
3520                 if (name != ""):
3521                     name += ", "
3522                 name += "module deps"
3523             if (printGenericMethods):
3524                 if (name != ""):
3525                     name += ", "
3526                 name += "generic methods"
3527             if (printNonGenericMethods):
3528                 if (name != ""):
3529                     name += ", "
3530                 name += "non generic methods"
3531
3532         print(offsetStr + name + " = [")
3533         for i in self._moduleOrMethodInfo:
3534             doPrint = (i.isModuleInfo() and printModuleDeps) or (i.isGenericMethodInfo() and printGenericMethods) \
3535                       or (i.isNonGenericMethodInfo() and printNonGenericMethods)
3536
3537             if (doPrint):
3538                 print(offsetStr + "  " + "{")
3539                 i.printRaw(offsetStr + "    ")
3540                 print(offsetStr + "  " + "},")
3541         print(offsetStr + "]")
3542
3543     # Print raw
3544     def printRaw(self, offsetStr=""):
3545         self.printRawHeader(offsetStr)
3546         self.printRawModules(offsetStr)
3547         self.printRawModuleOrMethodInfo(offsetStr)
3548
3549     # Print pretty header
3550     def printHeader(self, offsetStr=""):
3551         if (offsetStr is None or not issubclass(type(offsetStr), str)):
3552             raise InternalError()
3553
3554         self._header.print(offsetStr)
3555
3556     # Print pretty modules
3557     def printModules(self, offsetStr=""):
3558         if (offsetStr is None or not issubclass(type(offsetStr), str)):
3559             raise InternalError()
3560
3561         for i in self._modules:
3562             i.print(offsetStr)
3563
3564     # Print pretty module dependencies and methods
3565     def printModuleOrMethodInfo(self, offsetStr="", printModuleDeps=True,
3566                                 printGenericMethods=True, printNonGenericMethods=True):
3567         if (offsetStr is None or not issubclass(type(offsetStr), str)):
3568             raise InternalError()
3569
3570         if (printModuleDeps is None or printGenericMethods is None or printNonGenericMethods is None):
3571             raise InternalError()
3572
3573         if (not issubclass(type(printModuleDeps), bool) or not issubclass(type(printGenericMethods), bool)
3574             or not issubclass(type(printNonGenericMethods), bool)):
3575             raise InternalError()
3576
3577         for i in self._moduleOrMethodInfo:
3578             doPrint = (i.isModuleInfo() and printModuleDeps) or (i.isGenericMethodInfo() and printGenericMethods) \
3579                       or (i.isNonGenericMethodInfo() and printNonGenericMethods)
3580
3581             if (doPrint):
3582                 i.print(offsetStr, self._modules)
3583
3584     # Print pretty
3585     def print(self, offsetStr=""):
3586         self.printHeader(offsetStr)
3587         self.printModules(offsetStr)
3588         self.printModuleOrMethodInfo(offsetStr)
3589
3590     # Get length and type of record at specified byte
3591     @staticmethod
3592     def _getRecordTypeAndLen(bytesArr, index):
3593         if (bytesArr is None or index is None):
3594             raise InternalError()
3595
3596         if (not issubclass(type(bytesArr), list) or not issubclass(type(index), int)):
3597             raise InternalError()
3598
3599         if (index + RuntimeTypeSizes.int > len(bytesArr)):
3600             raise ProfileInconsistencyError()
3601
3602         data1 = Utility.mergeNBytes(bytesArr[index:index+RuntimeTypeSizes.int])
3603         rcdTyp = data1 >> RuntimeConstants.RECORD_TYPE_OFFSET
3604         rcdLen = 0
3605
3606         if (rcdTyp == RuntimeConstants.MULTICOREJIT_MODULE_RECORD_ID):
3607             rcdLen = data1 & RuntimeConstants.X_MODULE_RECORD_LEN_MASK
3608         elif (rcdTyp == RuntimeConstants.MULTICOREJIT_MODULEDEPENDENCY_RECORD_ID):
3609             rcdLen = RuntimeTypeSizes.int
3610         elif (rcdTyp == RuntimeConstants.MULTICOREJIT_METHOD_RECORD_ID):
3611             rcdLen = 2 * RuntimeTypeSizes.int
3612         elif (rcdTyp == RuntimeConstants.MULTICOREJIT_GENERICMETHOD_RECORD_ID):
3613             if (index + RuntimeTypeSizes.int + RuntimeTypeSizes.short > len(bytesArr)):
3614                 raise ProfileInconsistencyError()
3615
3616             tmpindex = index+RuntimeTypeSizes.int
3617             signatureLength = Utility.mergeNBytes(bytesArr[tmpindex:tmpindex+RuntimeTypeSizes.short])
3618             dataSize = signatureLength + RuntimeTypeSizes.int + RuntimeTypeSizes.short
3619             dataSize = Utility.alignUp(dataSize, RuntimeTypeSizes.int)
3620             rcdLen = dataSize
3621         else:
3622             raise ProfileInconsistencyError()
3623
3624         if ((index + rcdLen > len(bytesArr)) or ((rcdLen & 3) != 0)):
3625             raise ProfileInconsistencyError()
3626
3627         return rcdTyp, rcdLen
3628
3629     # Create from bytes
3630     @staticmethod
3631     def createFromBytes(bytesArr):
3632         if (bytesArr is None or not issubclass(type(bytesArr), list)):
3633             raise InternalError()
3634
3635         index = 0
3636         header = HeaderRecord.createFromBytes(bytesArr[index:index+HeaderRecord.Size])
3637         index += HeaderRecord.Size
3638
3639         modules = []
3640         moduleOrMethodInfo = []
3641
3642         while (index <= len(bytesArr) - RuntimeTypeSizes.int):
3643             rcdTyp, rcdLen = MCJProfile._getRecordTypeAndLen(bytesArr, index)
3644
3645             if (rcdTyp == RuntimeConstants.MULTICOREJIT_MODULE_RECORD_ID):
3646                 modules.append(ModuleRecordExtended.createFromBytes(bytesArr[index:index+rcdLen]))
3647             elif (rcdTyp == RuntimeConstants.MULTICOREJIT_MODULEDEPENDENCY_RECORD_ID):
3648                 moduleOrMethodInfo.append(InfoModuleDependency.createFromBytes(bytesArr[index:index+rcdLen]))
3649             elif (rcdTyp == RuntimeConstants.MULTICOREJIT_METHOD_RECORD_ID):
3650                 moduleOrMethodInfo.append(InfoNonGenericMethod.createFromBytes(bytesArr[index:index+rcdLen]))
3651             elif (rcdTyp == RuntimeConstants.MULTICOREJIT_GENERICMETHOD_RECORD_ID):
3652                 moduleOrMethodInfo.append(InfoGenericMethod.createFromBytes(bytesArr[index:index+rcdLen]))
3653             else:
3654                 raise InternalError()
3655
3656             index += rcdLen
3657
3658         if (index != len(bytesArr)):
3659             raise ProfileInconsistencyError()
3660
3661         return MCJProfile.createByRef(header, modules, moduleOrMethodInfo)
3662
3663     # Create from objects taking them by reference
3664     @staticmethod
3665     def createByRef(header, modules, moduleOrMethodInfo):
3666         profile = MCJProfile()
3667
3668         profile._header = header # pylint: disable=protected-access
3669         profile._modules = modules # pylint: disable=protected-access
3670         profile._moduleOrMethodInfo = moduleOrMethodInfo # pylint: disable=protected-access
3671
3672         profile.verify()
3673         return profile
3674
3675     # Create from objects taking them by copy
3676     @staticmethod
3677     def createByCopy(header, modules, moduleOrMethodInfo):
3678         newModules = []
3679         for i in modules:
3680             newModules.append(i.copy())
3681
3682         newModuleOrMethodInfo = []
3683         for i in moduleOrMethodInfo:
3684             newModuleOrMethodInfo.append(i.copy())
3685
3686         return MCJProfile.createByRef(header.copy(), newModules, newModuleOrMethodInfo)
3687
3688     # Copy object
3689     def copy(self):
3690         return MCJProfile.createByCopy(self._header, self._modules, self._moduleOrMethodInfo)
3691
3692     # Convert object to list of bytes
3693     def convertToBytes(self):
3694         bytesArr = []
3695
3696         bytesArr += self._header.convertToBytes()
3697
3698         for module in self._modules:
3699             bytesArr += module.convertToBytes()
3700
3701         for info in self._moduleOrMethodInfo:
3702             bytesArr += info.convertToBytes()
3703
3704         return bytesArr
3705
3706     # Split mcj profile in two: app-dependent (app) and app-independent (system)
3707     def split(self, systemModules): # pylint: disable=too-many-locals,too-many-statements,too-many-branches
3708         if (systemModules is None or not issubclass(type(systemModules), list)):
3709             raise InternalError()
3710
3711         for i in systemModules:
3712             if (i is None):
3713                 raise InternalError()
3714             if (not issubclass(type(i), str)):
3715                 raise InternalError()
3716
3717         cIdxApp = 0
3718         cIdxSys = 1
3719
3720         # App header at [0], sys header at [1]
3721         header = [self._header.copy(), self._header.copy()]
3722         # App modules at [0], sys modules at [1]
3723         modules = [[], []]
3724         # App moduleOrMethodInfo at [0], sys moduleOrMethodInfo at [1]
3725         moduleOrMethodInfo = [[], []]
3726         # App methodCount at [0], sys methodCount at [1]
3727         methodCount = [0, 0]
3728         # App moduleDepCount at [0], sys moduleDepCount at [1]
3729         moduleDepCount = [0, 0]
3730
3731         # Map from old module index to flag, whether module should be added to:
3732         # app profile at [0], system profile at [1]
3733         moduleMap = [[False] * len(self._modules), [False] * len(self._modules)]
3734         # Map from old method info index to flag, whether method info should be added to:
3735         # app profile (True) or to sys profile (False)
3736         methodInfoAppMap = [False] * len(self._moduleOrMethodInfo)
3737         # Map from old module index to new module index in:
3738         # app profile at [0], system profile at [1]
3739         moduleIndexMap = [[None] * len(self._modules), [None] * len(self._modules)]
3740
3741         # I. ==== Create map of system modules ====
3742
3743         # Map from system module name to flag True
3744         systemModulesMap = {}
3745         for sysmodule in systemModules:
3746             systemModulesMap[sysmodule] = True
3747
3748         # II. ==== Go through all modules and mark system modules ====
3749
3750         for index, module in enumerate(self._modules):
3751             moduleMap[cIdxSys][index] = module.getModuleName() in systemModulesMap
3752
3753         # III. ==== Go through all method infos and create lists of module indexes for system and app profiles
3754
3755         for index, value in enumerate(moduleMap[cIdxSys]):
3756             moduleMap[cIdxApp][index] = not value
3757
3758         for index, info in enumerate(self._moduleOrMethodInfo):
3759             if (info.isMethodInfo()):
3760                 indexes = info.getAllModuleIndexes()
3761
3762                 for i in indexes:
3763                     if (not moduleMap[cIdxSys][i]):
3764                         methodInfoAppMap[index] = True
3765                         break
3766
3767                 if (methodInfoAppMap[index]):
3768                     # mark all modules as requiremens for app profile
3769                     for i in indexes:
3770                         moduleMap[cIdxApp][i] = True
3771
3772         # IV. === Go through all modules again and add to profiles accordingly ====
3773
3774         for index, module in enumerate(self._modules):
3775
3776             isAdded = False
3777
3778             # add to app and system profiles
3779             for i in range(2):
3780                 if (moduleMap[i][index]):
3781                     newModule = module.copy()
3782                     moduleIndexMap[i][index] = len(modules[i])
3783                     modules[i].append(newModule)
3784                     isAdded = True
3785
3786             if (not isAdded):
3787                 raise InternalError()
3788
3789         # V. ==== Go through all infos again and add to profiles accordingly ====
3790
3791         for index, info in enumerate(self._moduleOrMethodInfo):
3792             isAdded = False
3793
3794             # add to app and system profiles
3795             for i in range(2):
3796                 doAddModule = info.isModuleInfo() and moduleMap[i][info.getModuleIndex()]
3797                 doAddMethodApp = (i == cIdxApp) and methodInfoAppMap[index]
3798                 doAddMethodSys = (i == cIdxSys) and not methodInfoAppMap[index]
3799
3800                 doAdd = doAddModule or (info.isMethodInfo() and (doAddMethodApp or doAddMethodSys))
3801
3802                 if (doAdd):
3803                     if (info.isMethodInfo() and isAdded):
3804                         raise InternalError()
3805
3806                     newInfo = info.copy()
3807                     newInfo.updateModuleIndex(moduleIndexMap[i])
3808                     moduleOrMethodInfo[i].append(newInfo)
3809
3810                     if (info.isModuleInfo()):
3811                         moduleDepCount[i] += 1
3812                     elif (info.isMethodInfo()):
3813                         methodCount[i] += 1
3814                     else:
3815                         raise InternalError()
3816
3817                     isAdded = True
3818
3819             if (not isAdded):
3820                 raise InternalError()
3821
3822         # VI. ==== Recalculate jitMethodCount ====
3823
3824         for index in range(2):
3825             for module in modules[index]:
3826                 module.getModuleRecord().setJitMethodCount(0)
3827
3828             for info in moduleOrMethodInfo[index]:
3829                 if (info.isMethodInfo()):
3830                     indexes = info.getAllModuleIndexes()
3831                     for i in indexes:
3832                         moduleRecord = modules[index][i].getModuleRecord()
3833                         count = moduleRecord.getJitMethodCount()
3834                         moduleRecord.setJitMethodCount(count + 1)
3835
3836         # VII. ==== Initialize new headers ====
3837
3838         for index in range(2):
3839             header[index].setModuleCount(len(modules[index]))
3840             header[index].setMethodCount(methodCount[index])
3841             header[index].setModuleDepCount(moduleDepCount[index])
3842             header[index].dropGlobalUsageStats()
3843
3844         # VIII. ==== Perform some consistency checks ====
3845
3846         if (methodCount[0] + methodCount[1] != self._header.getMethodCount()):
3847             raise InternalError()
3848
3849         # IX. ==== Create new profiles ====
3850
3851         mcjProfileApp = MCJProfile.createByRef(header[0], modules[0], moduleOrMethodInfo[0])
3852         mcjProfileSys = MCJProfile.createByRef(header[1], modules[1], moduleOrMethodInfo[1])
3853
3854         return mcjProfileApp, mcjProfileSys
3855
3856     # Merge new mcj profile with existing one
3857     def merge(self, mcjProfile): # pylint: disable=too-many-locals,too-many-statements
3858         if (mcjProfile is None or not issubclass(type(mcjProfile), MCJProfile)):
3859             raise InternalError()
3860
3861         # Map from module name in self to module index
3862         moduleNameMap = {}
3863         # Map from str method representation to info index
3864         methodMap = []
3865         # Map from old module index in mcjProfile to previous max module level in self (if existed, otherwise None)
3866         moduleLoadLevelMap = [None] * len(mcjProfile.getModules())
3867         # Map from old module index in mcjProfile to new module index in self profile
3868         moduleIndexMap = [None] * len(mcjProfile.getModules())
3869
3870         # I. ==== Create map of existing module names ====
3871
3872         for index, module in enumerate(self._modules):
3873             moduleNameMap[module.getModuleName()] = index
3874
3875         # II. ==== Merge modules ====
3876
3877         # Merge modules: add new modules if needed, update max load level for existing modules
3878         for index, newmodule in enumerate(mcjProfile.getModules()):
3879             # check if modules match by simple name, because module search is performed by it
3880             existingModuleIndex = moduleNameMap.get(newmodule.getModuleName())
3881
3882             if (not existingModuleIndex is None):
3883                 # exists, update max load level (if load levels do not match, use max one)
3884                 existingModule = self._modules[existingModuleIndex]
3885
3886                 if (existingModule.getAssemblyName() != newmodule.getAssemblyName()):
3887                     print("Can't merge profiles: two modules with same names but different assembly names!")
3888                     raise UnsupportedError()
3889                 if (existingModule.getModuleRecord().getVersion() != newmodule.getModuleRecord().getVersion()):
3890                     print("Can't merge profiles: two modules with same names but different versions!")
3891                     raise UnsupportedError()
3892                 if (existingModule.getModuleRecord().getFlags() != newmodule.getModuleRecord().getFlags()):
3893                     print("Can't merge profiles: two modules with same names but different flags!")
3894                     raise UnsupportedError()
3895
3896                 existingModuleLoadLevel = existingModule.getModuleRecord().getLoadLevel()
3897                 moduleLoadLevelMap[index] = existingModuleLoadLevel
3898                 moduleIndexMap[index] = existingModuleIndex
3899                 existingModule.getModuleRecord().setLoadLevel(max(newmodule.getModuleRecord().getLoadLevel(),
3900                                                                   existingModuleLoadLevel))
3901             else:
3902                 # simple module names do not match, safe to add new module
3903                 moduleLoadLevelMap[index] = None
3904                 moduleIndexMap[index] = len(self._modules)
3905                 newmoduleToAdd = newmodule.copy()
3906                 self._modules.append(newmoduleToAdd)
3907
3908         # III. ==== Fill method map ====
3909
3910         for module in self._modules:
3911             methodMap.append({})
3912
3913         # Prepare map from method str representation to index of info
3914         for index, info in enumerate(self._moduleOrMethodInfo):
3915             moduleIndex = info.getModuleIndex()
3916
3917             if (info.isNonGenericMethodInfo()):
3918                 methodMap[moduleIndex][str(info.getMethodToken())] = index
3919             elif (info.isGenericMethodInfo()):
3920                 methodMap[moduleIndex][info.getMethodHandle().getStr(self._modules)] = index
3921
3922         # IV. ==== Merge infos ====
3923
3924         # Merge module and method info (existing part of self doesn't change, simply add mcjProfile after self)
3925         for newinfo in mcjProfile.getModuleOrMethodInfo():
3926             if (newinfo.isMethodInfo()):
3927                 infoIndex = None
3928
3929                 newModuleIndex = moduleIndexMap[newinfo.getModuleIndex()]
3930
3931                 if (newinfo.isNonGenericMethodInfo()):
3932                     infoIndex = methodMap[newModuleIndex].get(str(newinfo.getMethodToken()))
3933                 elif (newinfo.isGenericMethodInfo()):
3934                     infoIndex = methodMap[newModuleIndex].get(newinfo.getMethodHandle().getStr(mcjProfile.getModules()))
3935                 else:
3936                     raise InternalError()
3937
3938                 if (infoIndex is None):
3939                     newinfoToAdd = newinfo.copy()
3940                     newinfoToAdd.updateModuleIndex(moduleIndexMap)
3941                     self._moduleOrMethodInfo.append(newinfoToAdd)
3942                 else:
3943                     if (newinfo.getMethodFlags() != self._moduleOrMethodInfo[infoIndex].getMethodFlags()):
3944                         print("Can't merge profiles: two methods with same token/signature but different flags!")
3945                         raise UnsupportedError()
3946             else:
3947                 oldExistingLoadLevel = moduleLoadLevelMap[newinfo.getModuleIndex()]
3948
3949                 # module dependency
3950                 if ((not oldExistingLoadLevel is None) and oldExistingLoadLevel >= newinfo.getModuleLoadLevel()):
3951                     # nothing to do, don't add module dependency, already loaded at required level
3952                     pass
3953                 else:
3954                     newinfoToAdd = newinfo.copy()
3955                     newinfoToAdd.updateModuleIndex(moduleIndexMap)
3956                     self._moduleOrMethodInfo.append(newinfoToAdd)
3957
3958         # IV. ==== Set stats ====
3959
3960         methodCount = 0
3961         moduleDepCount = 0
3962
3963         # Reset method count
3964         for module in self._modules:
3965             module.getModuleRecord().setJitMethodCount(0)
3966
3967         # Update stats in modules
3968         for info in self._moduleOrMethodInfo:
3969             if (info.isMethodInfo()):
3970                 methodCount += 1
3971
3972                 indexes = info.getAllModuleIndexes()
3973                 for i in indexes:
3974                     moduleRecord = self._modules[i].getModuleRecord()
3975                     count = moduleRecord.getJitMethodCount()
3976                     moduleRecord.setJitMethodCount(count + 1)
3977             else:
3978                 moduleDepCount += 1
3979
3980         # update stats in headers
3981         self._header.setModuleCount(len(self._modules))
3982         self._header.setMethodCount(methodCount)
3983         self._header.setModuleDepCount(moduleDepCount)
3984         # new profile was not used yet, so drop usage stats
3985         self._header.dropGlobalUsageStats()
3986
3987         self.verify()
3988
3989     # Find module by name
3990     def findModuleByName(self, moduleName):
3991         if (moduleName is None or not issubclass(type(moduleName), str)):
3992             raise InternalError()
3993         for module in self._modules:
3994             if (moduleName == module.getModuleName()):
3995                 return module
3996         return None
3997
3998     # Find method by token
3999     def findMethodByToken(self, methodToken):
4000         if (methodToken is None or not issubclass(type(methodToken), int)):
4001             raise InternalError()
4002         for moduleOrMethodInfo in self._moduleOrMethodInfo:
4003             if (moduleOrMethodInfo.isMethodInfo() and moduleOrMethodInfo.getMethodToken() == methodToken):
4004                 return moduleOrMethodInfo
4005         return None
4006
4007     # Read MCJ profile from file
4008     @staticmethod
4009     def readFromFile(inputFile):
4010         file = open(inputFile, "rb")
4011         bytesArr = list(file.read())
4012         file.close()
4013
4014         mcjProfile = MCJProfile.createFromBytes(bytesArr)
4015
4016         return mcjProfile
4017
4018     # Write MCJ profile to file
4019     @staticmethod
4020     def writeToFile(outputFile, mcjProfile):
4021         file = open(outputFile, "wb")
4022         file.write(bytearray(mcjProfile.convertToBytes()))
4023         file.close()
4024
4025 # ----------------------------------------------------------------------------------------------------------------------
4026 class CLI:
4027
4028     # Constructor with command line arguments
4029     def __init__(self, args):
4030         self._args = args
4031
4032     # Split mcj profiles in two: app-dependent (app) and app-independent (system)
4033     def commandSplit(self):
4034         systemModulesFile = open(self._args.system_modules_list, "r")
4035         systemModules = systemModulesFile.readlines()
4036         systemModulesFile.close()
4037
4038         for i, module in enumerate(systemModules):
4039             systemModules[i] = module.rstrip("\n")
4040
4041         for filepath in self._args.input:
4042             outFilepathApp = filepath + ".app"
4043             outFilepathSys = filepath + ".sys"
4044
4045             mcjProfile = MCJProfile.readFromFile(filepath)
4046             mcjProfileApp, mcjProfileSys = mcjProfile.split(systemModules)
4047
4048             MCJProfile.writeToFile(outFilepathApp, mcjProfileApp)
4049             MCJProfile.writeToFile(outFilepathSys, mcjProfileSys)
4050
4051             print("MCJ profile " + filepath + " was split in:")
4052             print("  1. app-dependent " + outFilepathApp)
4053             print("  2. app-independent " + outFilepathSys)
4054             print("")
4055
4056     # Merge mcj profiles
4057     def commandMerge(self):
4058         mcjProfileBase = MCJProfile.readFromFile(self._args.input[0])
4059
4060         for index in range(1, len(self._args.input)):
4061             mcjProfile = MCJProfile.readFromFile(self._args.input[index])
4062             mcjProfileBase.merge(mcjProfile)
4063
4064         MCJProfile.writeToFile(self._args.output, mcjProfileBase)
4065
4066         print("MCJ profiles " + str(self._args.input) + " were merged in: " + self._args.output)
4067         print("")
4068
4069     # Verify mcj profiles format consistency
4070     def commandVerify(self):
4071         for filepath in self._args.input:
4072             mcjProfile = MCJProfile.readFromFile(filepath) # pylint: disable=unused-variable
4073             print("MCJ profile " + filepath + " is correct!")
4074
4075     # Find module or method in mcj profile
4076     def commandFind(self):
4077         for filepath in self._args.input:
4078             mcjProfile = MCJProfile.readFromFile(filepath)
4079
4080             if (not self._args.module is None):
4081                 for module in self._args.module:
4082                     if (not mcjProfile.findModuleByName(module) is None):
4083                         print("MCJ profile " + filepath + " contains module " + module)
4084                     else:
4085                         print("MCJ profile " + filepath + " does not contain module " + module)
4086
4087             if (not self._args.method_token is None):
4088                 for method in self._args.method_token:
4089                     methodToken = int(method)
4090                     if (not mcjProfile.findMethodByToken(methodToken) is None):
4091                         print("MCJ profile " + filepath + " contains method with token " + method)
4092                     else:
4093                         print("MCJ profile " + filepath + " does not contain method with token " + method)
4094
4095     # Compare multiple mcj profiles for equality
4096     def commandCompare(self):
4097         if (self._args.sha256):
4098             resBase = subprocess.run(["sha256sum", self._args.input[0]], check=True,
4099                                      stdout=subprocess.PIPE, stderr=subprocess.PIPE)
4100             shaBase = resBase.stdout.decode("ascii").split(" ")[0]
4101             for index in range(1, len(self._args.input)):
4102                 res = subprocess.run(["sha256sum", self._args.input[index]], check=True,
4103                                      stdout=subprocess.PIPE, stderr=subprocess.PIPE)
4104                 sha = res.stdout.decode("ascii").split(" ")[0]
4105                 resultStr = " are equal by hash." if sha == shaBase else " are not equal by hash."
4106                 print("MCJ profiles " + self._args.input[0] + " and " + self._args.input[index] + resultStr)
4107         else:
4108             mcjProfileBase = MCJProfile.readFromFile(self._args.input[0])
4109             for index in range(1, len(self._args.input)):
4110                 mcjProfile = MCJProfile.readFromFile(self._args.input[index])
4111                 resultStr = " are equal." if mcjProfile == mcjProfileBase else " are not equal."
4112                 print("MCJ profiles " + self._args.input[0] + " and " + self._args.input[index] + resultStr)
4113
4114     # Clean usage stats in profile
4115     def commandCleanStats(self):
4116         for filepath in self._args.input:
4117             outfilepath = filepath + ".cleaned"
4118
4119             mcjProfile = MCJProfile.readFromFile(filepath)
4120             mcjProfile.getHeader().dropGlobalUsageStats()
4121             MCJProfile.writeToFile(outfilepath, mcjProfile)
4122
4123             print("Cleaned usage stats: " + filepath + " is saved as " + outfilepath)
4124             print("")
4125
4126     def commandPrint(self):
4127         for filepath in self._args.input:
4128             print("============ MCJ profile " + filepath + " ============")
4129             mcjProfile = MCJProfile.readFromFile(filepath)
4130
4131             doPrintModuleDeps = self._args.module_deps
4132             doPrintGenericMethods = self._args.methods or self._args.generics
4133             doPrintNonGenericMethods = self._args.methods or self._args.non_generics
4134             doPrintModuleOrMethodInfo = doPrintModuleDeps or doPrintGenericMethods or doPrintNonGenericMethods
4135
4136             if (self._args.raw):
4137                 # Print raw mcj profile
4138                 if (self._args.header):
4139                     mcjProfile.printRawHeader()
4140                 if (self._args.modules):
4141                     mcjProfile.printRawModules()
4142                 if (doPrintModuleOrMethodInfo):
4143                     mcjProfile.printRawModuleOrMethodInfo("", doPrintModuleDeps, doPrintGenericMethods,
4144                                                           doPrintNonGenericMethods)
4145                 if (not self._args.header and not self._args.modules and not doPrintModuleOrMethodInfo):
4146                     mcjProfile.printRaw()
4147             else:
4148                 # Pretty-print mcj profile
4149                 if (self._args.header):
4150                     mcjProfile.printHeader()
4151                 if (self._args.modules):
4152                     mcjProfile.printModules()
4153                 if (doPrintModuleOrMethodInfo):
4154                     mcjProfile.printModuleOrMethodInfo("", doPrintModuleDeps, doPrintGenericMethods,
4155                                                        doPrintNonGenericMethods)
4156                 if (not self._args.header and not self._args.modules and not doPrintModuleOrMethodInfo):
4157                     mcjProfile.print()
4158
4159     # Show short summary on mcj profile format
4160     def commandHelp(self): # pylint: disable=no-self-use,too-many-statements
4161         print("! To show command options help use --help option !")
4162         print("")
4163
4164         if (self._args.mcj_format):
4165             print(">>> MCJ format help.")
4166             print("")
4167             print("MCJ file is a binary file with next sections:")
4168             print("1. Header, i.e. 'HeaderRecord'")
4169             print("2. Modules, i.e. multiple 'ModuleRecord'")
4170             print("3. Methods and Module dependencies, i.e. multiple 'JitInfRecord':")
4171             print("  'ModuleDependency', 'GenericMethod', 'NonGenericMethod'")
4172             print("")
4173             print("I. Header.")
4174             print("")
4175             print("Header contains:")
4176             print("1. MULTICOREJIT_HEADER_RECORD_ID tag")
4177             print("2. Version of profile, MULTICOREJIT_PROFILE_VERSION")
4178             print("3. Number of used modules, module dependencies and methods")
4179             print("4. Profile usage stats (0 if profile was not written after usage)")
4180             print("")
4181             print("II. Modules.")
4182             print("")
4183             print("These section contains multiple ModuleRecord, each ModuleRecord contains:")
4184             print("1. MULTICOREJIT_MODULE_RECORD_ID tag")
4185             print("2. Module version")
4186             print("3. Number of methods from module in profile")
4187             print("4. Final load level for module")
4188             print("5. Simple name of module")
4189             print("6. Assembly name")
4190             print("")
4191             print("III. Modules and Methods dependencies")
4192             print("")
4193             print("This section contains multiple JitInfRecord, each JitInfRecord can be:")
4194             print("  Module dependency, non-generic Method or generic Method.")
4195             print("")
4196             print("Each Module dependency contains:")
4197             print("1. Module index")
4198             print("2. Current module load level")
4199             print("3. MULTICOREJIT_MODULEDEPENDENCY_RECORD_ID tag")
4200             print("")
4201             print("Each non-generic Method contains:")
4202             print("1. Module index")
4203             print("2. Method flags")
4204             print("3. MULTICOREJIT_METHOD_RECORD_ID tag")
4205             print("4. Method token from dll")
4206             print("")
4207             print("Each generic Method contains:")
4208             print("1. Module index")
4209             print("2. Method flags")
4210             print("3. MULTICOREJIT_GENERICMETHOD_RECORD_ID tag")
4211             print("4. Length of binary signature for method")
4212             print("5. Binary signature for method")
4213         elif (self._args.binary_signature_format):
4214             print(">>> Binary signature format help.")
4215             print("")
4216             print("I. Value encoding")
4217             print("")
4218             print("Binary signature for method (as well as binary signature for type) is a byte array.")
4219             print("")
4220             print("Each value that is stored in this byte array is compacted (see _encodeValue) (except plain bytes):")
4221             print("  - values '<= 0x7f' are stored as 1 byte without changes")
4222             print("  - values '> 0x7f and <= 0x3fff' are stored as 2 bytes in big endian order (1st byte | 0x80)")
4223             print("  - values '> 0x3fff and <= 0x1fffffff' are stored as 4 bytes in big endian order (1st byte | 0xc0)")
4224             print("As a result of this 3 high bits of 1st byte determine length of value:")
4225             print("  - 0yz mean 1 byte")
4226             print("  - 10y mean 2 bytes")
4227             print("  - 110 mean 4 bytes")
4228             print("As mentioned above, sometimes plain bytes are saved (see _encodeByte).")
4229             print("")
4230             print("Tokens (method, type, etc.) are stored as plain values (see above) or tokens (see _encodeToken):")
4231             print("  1. rid and typ are obtained from token")
4232             print("  2. rid is shifted left on 2 bits")
4233             print("  3. rid is ORed with special 2bit constant determined by typ")
4234             print("  4. resulting modified rid goes through value encoding procedure above")
4235             print("As a result of this typ of token is determined by 2 lowest bits of saved value.")
4236             print("")
4237             print("II. Type signature encoding")
4238             print("")
4239             print("Each type is encoded using logic mentioned in previous section.")
4240             print("")
4241             print("This logic is pretty complex, but main building blocks of all types are:")
4242             print("  - other types")
4243             print("  - type tokens")
4244             print("  - calling convention")
4245             print("  - number of func args")
4246             print("  - number of generic args")
4247             print("  - array rank")
4248             print("  - module index <-- this is module index in array of modules that are processed by MCJ!")
4249             print("For more details see source code (see _encodeSignatureForTypeHandle).")
4250             print("")
4251             print("III. Method signature encoding")
4252             print("")
4253             print("Each method is encoded using logic mentioned in previous section.")
4254             print("")
4255             print("Method encoding order:")
4256             print("  1. Method flags")
4257             print("  2. Type from which this method comes from")
4258             print("  3. Method token")
4259             print("  4*. Number of method instantion args (for generic methods)")
4260             print("  5*. Types of all method instantion args (for generic methods)")
4261             print("For more details see source code (see encodeMethod).")
4262         else:
4263             print("! To show format help use --mcj-format or --binary-signature-format options !")
4264
4265     # Perform various self testing
4266     def commandSelfTest(self): # pylint: disable=too-many-locals,too-many-statements
4267         if (self._args.rw):
4268             # Read mcj profile, write it to file, read it back again and compare
4269             for filepath in self._args.input:
4270                 outFilepath = filepath + ".__tmp"
4271                 mcjProfileIn = MCJProfile.readFromFile(filepath)
4272                 MCJProfile.writeToFile(outFilepath, mcjProfileIn)
4273                 mcjProfileOut = MCJProfile.readFromFile(outFilepath)
4274                 if (mcjProfileIn == mcjProfileOut):
4275                     print("Read-write self test passed for " + filepath)
4276                 else:
4277                     print("Read-write self test failed for " + filepath)
4278         elif (self._args.rw_sha256):
4279             # Read mcj profile, write it to file, compare with sha256sum with original file
4280             for filepath in self._args.input:
4281                 outFilepath = filepath + ".__tmp"
4282                 mcjProfile = MCJProfile.readFromFile(filepath)
4283                 MCJProfile.writeToFile(outFilepath, mcjProfile)
4284
4285                 inputRes = subprocess.run(["sha256sum", filepath], check=True,
4286                                           stdout=subprocess.PIPE, stderr=subprocess.PIPE)
4287                 outputRes = subprocess.run(["sha256sum", outFilepath], check=True,
4288                                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
4289
4290                 inputSha = inputRes.stdout.decode("ascii").split(" ")[0]
4291                 outputSha = outputRes.stdout.decode("ascii").split(" ")[0]
4292
4293                 if (inputSha == outputSha):
4294                     print("Read-write sha256sum self test passed for " + filepath)
4295                 else:
4296                     print("Read-write sha256sum self test failed for " + filepath)
4297         elif (self._args.sm):
4298             # Split mcj profile, merge two resulting profiles, split again and compare
4299             systemModulesFile = open(self._args.system_modules_list, "r")
4300             systemModules = systemModulesFile.readlines()
4301             systemModulesFile.close()
4302
4303             for i, moduleName in enumerate(systemModules):
4304                 systemModules[i] = moduleName.rstrip("\n")
4305
4306             for filepath in self._args.input:
4307                 mcjProfile = MCJProfile.readFromFile(filepath)
4308                 mcjProfileApp, mcjProfileSys = mcjProfile.split(systemModules)
4309
4310                 prevMergedAppFirst = mcjProfile
4311                 prevMergedSysFirst = mcjProfile
4312                 prevSplitAppFirstApp = mcjProfileApp
4313                 prevSplitAppFirstSys = mcjProfileSys
4314                 prevSplitSysFirstApp = mcjProfileApp
4315                 prevSplitSysFirstSys = mcjProfileSys
4316
4317                 isCorrect = True
4318                 depthCheck = 1
4319
4320                 # Note: first split&merge won't match because order changes, but it stabilizes on 2nd merge
4321                 for depth in range(1,5):
4322                     # Merge two prev profiles
4323                     mergedAppFirst = prevSplitAppFirstApp.copy()
4324                     mergedAppFirst.merge(prevSplitAppFirstSys)
4325
4326                     mergedSysFirst = prevSplitSysFirstSys.copy()
4327                     mergedSysFirst.merge(prevSplitSysFirstApp)
4328
4329                     if (depth > depthCheck
4330                         and (mergedAppFirst != prevMergedAppFirst or mergedSysFirst != prevMergedSysFirst)):
4331                         isCorrect = False
4332                         break
4333
4334                     prevMergedAppFirst = mergedAppFirst
4335                     prevMergedSysFirst = mergedSysFirst
4336
4337                     mergedAppFirst2 = mergedAppFirst.copy()
4338                     mergedAppFirst2.merge(mergedSysFirst)
4339
4340                     mergedSysFirst2 = mergedSysFirst.copy()
4341                     mergedSysFirst2.merge(mergedAppFirst)
4342
4343                     if (mergedAppFirst2 != mergedAppFirst or mergedSysFirst2 != mergedSysFirst):
4344                         isCorrect = False
4345                         break
4346
4347                     # Split cur profiles
4348                     splitAppFirstApp, splitAppFirstSys = mergedAppFirst.split(systemModules)
4349                     splitSysFirstApp, splitSysFirstSys = mergedSysFirst.split(systemModules)
4350
4351                     if (depth > depthCheck
4352                         and (splitAppFirstApp != prevSplitAppFirstApp or splitAppFirstSys != prevSplitAppFirstSys
4353                              or splitSysFirstApp != prevSplitSysFirstApp or splitSysFirstSys != prevSplitSysFirstSys)):
4354                         isCorrect = False
4355                         break
4356
4357                     prevSplitAppFirstApp = splitAppFirstApp
4358                     prevSplitAppFirstSys = splitAppFirstSys
4359                     prevSplitSysFirstApp = splitSysFirstApp
4360                     prevSplitSysFirstSys = splitSysFirstSys
4361
4362                     splitAppFirstApp2, splitAppFirstSys2 = splitAppFirstApp.split(systemModules)
4363
4364                     if (splitAppFirstApp2 != splitAppFirstApp):
4365                         isCorrect = False
4366                         break
4367
4368                     splitAppFirstApp2, splitAppFirstSys2 = splitAppFirstSys.split(systemModules)
4369
4370                     if (splitAppFirstSys2 != splitAppFirstSys):
4371                         isCorrect = False
4372                         break
4373
4374                     splitSysFirstApp2, splitSysFirstSys2 = splitSysFirstApp.split(systemModules)
4375
4376                     if (splitSysFirstApp2 != splitSysFirstApp):
4377                         isCorrect = False
4378                         break
4379
4380                     splitSysFirstApp2, splitSysFirstSys2 = splitSysFirstSys.split(systemModules)
4381
4382                     if (splitSysFirstSys2 != splitSysFirstSys):
4383                         isCorrect = False
4384                         break
4385
4386                 if (isCorrect):
4387                     print("Split-merge self test passed for " + filepath)
4388                 else:
4389                     print("Split-merge self test failed for " + filepath)
4390         elif (self._args.unit):
4391             # TODO: add unit tests
4392             pass
4393         else:
4394             pass
4395
4396 # ----------------------------------------------------------------------------------------------------------------------
4397
4398 def main():
4399     parser = argparse.ArgumentParser()
4400
4401     commands = "split, merge, verify, find, compare, clean-stats, print, help, self-test"
4402
4403     parser.add_argument("command", help="Command to execute: " + commands)
4404
4405     # Overall options
4406     parser.add_argument("-i", "--input", help="Input mcj profiles", action="append")
4407
4408     # Split options
4409     parser.add_argument("--system-modules-list", help="[split], file with app-independent (i.e. system) module names")
4410
4411     # Merge options
4412     parser.add_argument("-o", "--output", help="[merge], output mcj profile")
4413
4414     # Find options
4415     parser.add_argument("--module", help="[find], name of module to find in mcj profile", action="append")
4416     parser.add_argument("--method-token", help="[find], token of method to find in mcj profile", action="append")
4417
4418     # Compare options
4419     parser.add_argument("--sha256", help="[compare], compare mcj profiles using sha256sum", action="store_true")
4420
4421     # Print options
4422     parser.add_argument("--raw", help="[print], print raw mcj profile", action="store_true")
4423     parser.add_argument("--header", help="[print], print header of mcj profile", action="store_true")
4424     parser.add_argument("--modules", help="[print], print modules in mcj profile", action="store_true")
4425
4426     parser.add_argument("--methods", help="[print], print methods in mcj profile", action="store_true")
4427     parser.add_argument("--generics", help="[print], print generic methods in mcj profile", action="store_true")
4428     parser.add_argument("--non-generics", help="[print], print non-generic methods in mcj profile", action="store_true")
4429     parser.add_argument("--module-deps", help="[print], print module dependencies in mcj profile", action="store_true")
4430
4431     # Help options
4432     parser.add_argument("--mcj-format", help="[help], show help on mcj format", action="store_true")
4433     parser.add_argument("--binary-signature-format", help="[help], show help on binary signature format",
4434                         action="store_true")
4435
4436     # Self test option
4437     parser.add_argument("--rw", help="[self-test], perform read-write self-test", action="store_true")
4438     parser.add_argument("--rw-sha256",
4439                         help="[self-test], perform read-write self-test using sha256sum", action="store_true")
4440     parser.add_argument("--sm", help="[self-test], perform split-merge self-test", action="store_true")
4441     parser.add_argument("--unit", help="TODO, [self-test], perform unit testing self-test", action="store_true")
4442
4443     args = parser.parse_args()
4444
4445     cli = CLI(args)
4446
4447     if (args.command == "split"):
4448         cli.commandSplit()
4449     elif (args.command == "merge"):
4450         cli.commandMerge()
4451     elif (args.command == "verify"):
4452         cli.commandVerify()
4453     elif (args.command == "find"):
4454         cli.commandFind()
4455     elif (args.command == "compare"):
4456         cli.commandCompare()
4457     elif (args.command == "clean-stats"):
4458         cli.commandCleanStats()
4459     elif (args.command == "print"):
4460         cli.commandPrint()
4461     elif (args.command == "help"):
4462         cli.commandHelp()
4463     elif (args.command == "self-test"):
4464         cli.commandSelfTest()
4465
4466 if __name__ == "__main__":
4467     main()