1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 /***************************************************************************/
7 /***************************************************************************/
11 #include <debugmacros.h> // for ASSERTE
12 #include "ilformatter.h"
13 #include "outstring.h"
16 /***************************************************************************/
17 void ILFormatter::init(IMetaDataImport* aMeta, const BYTE* aStart,
18 const BYTE* aLimit, unsigned maxStack, const COR_ILMETHOD_SECT_EH* eh) {
19 this->~ILFormatter(); // clean out old stuff
24 if (maxStack == 0) maxStack++;
25 stackStart = stackCur = new StackEntry[maxStack];
26 stackEnd = stackStart + maxStack;
27 targetStart = targetCur = targetEnd = 0;
29 COR_ILMETHOD_SECT_EH_CLAUSE_FAT buff;
30 const COR_ILMETHOD_SECT_EH_CLAUSE_FAT* clause;
31 for(unsigned i = 0; i < eh->EHCount(); i++) {
32 clause = (COR_ILMETHOD_SECT_EH_CLAUSE_FAT*)eh->EHClause(i, &buff);
33 // is it a regular catch clause ?
34 if ((clause->GetFlags() & (COR_ILEXCEPTION_CLAUSE_FINALLY | COR_ILEXCEPTION_CLAUSE_FAULT)) == 0)
35 setTarget(clause->GetHandlerOffset(), 1);
36 if(clause->GetFlags() & COR_ILEXCEPTION_CLAUSE_FILTER)
37 setTarget(clause->GetFilterOffset(), 1);
42 /***************************************************************************/
43 inline size_t ILFormatter::stackDepth() {
44 return(stackCur - stackStart);
47 /***************************************************************************/
48 inline void ILFormatter::pushAndClear(OutString* val, int prec) {
49 if (stackCur >= stackEnd) {
50 _ASSERTE(!"Stack Overflow (can be ignored)");
51 return; // Ignore overflow in free build
53 stackCur->val.swap(*val);
55 stackCur->prec = prec;
59 /***************************************************************************/
60 inline OutString* ILFormatter::top() {
61 if (stackDepth() == 0) {
62 _ASSERTE(!"Stack underflow (can be ignored)");
63 stackStart->val.clear();
64 stackStart->val << "<UNDERFLOW ERROR>";
65 return (&stackStart->val);
67 return(&stackCur[-1].val);
70 /***************************************************************************/
71 inline OutString* ILFormatter::pop(int prec) {
72 if (stackDepth() == 0) {
73 _ASSERTE(!"Stack underflow (can be ignored)");
74 stackStart->val.clear();
75 stackStart->val << "<UNDERFLOW ERROR>";
76 return (&stackStart->val);
79 if (stackCur->prec < prec) {
80 stackCur->val.prepend('(');
83 return(&stackCur->val);
86 /***************************************************************************/
87 inline void ILFormatter::popN(size_t num) {
88 if (stackCur-stackStart < (SSIZE_T)num) {
89 _ASSERTE(!"Stack underflow (can be ignored)");
90 stackCur = stackStart;
96 /***************************************************************************/
97 void ILFormatter::setStackAsTarget(size_t ilOffset) {
99 Target*ptr = targetStart;
101 if (ptr >= targetCur)
103 if (ptr->ilOffset == ilOffset)
108 for(size_t i = 0; i < ptr->stackDepth; i++) {
109 stackStart[i].val.clear();
110 stackStart[i].val << "@STK" << (unsigned)i;
112 stackCur = stackStart + ptr->stackDepth;
115 /***************************************************************************/
116 void ILFormatter::setTarget(size_t ilOffset, size_t depth) {
120 if (targetCur >= targetEnd) {
121 Target* targetOld = targetStart;
122 size_t oldLen = targetCur-targetStart;
123 targetStart = new Target[oldLen+10];
124 targetEnd = &targetStart[oldLen+10];
125 targetCur = &targetStart[oldLen];
126 memcpy(targetStart, targetOld, sizeof(Target)*oldLen);
129 targetCur->ilOffset = ilOffset;
130 targetCur->stackDepth = depth;
134 /***************************************************************************/
135 void ILFormatter::spillStack(OutString* out) {
137 for(unsigned i = 0; i < stackDepth(); i++) {
138 // don't bother spilling something already spilled.
139 if (memcmp(stackStart[i].val.val(), "@STK", 4) != 0)
140 *out << "@STK" << i << " = " << stackStart[i].val.val() << "\n";
141 stackStart[i].val.clear();
142 stackStart[i].val << "@STK" << i ;
146 /***************************************************************************/
147 const BYTE* ILFormatter::formatInstr(const BYTE* instrPtr, OutString* out) {
149 _ASSERTE(start < instrPtr && instrPtr < limit);
152 instrPtr = op.fetch(instrPtr, &arg);
153 *out << op.getName();
154 if (op.getArgsInfo() != InlineNone)
156 formatInstrArgs(op, arg, out, instrPtr - start);
160 /***************************************************************************/
161 void ILFormatter::formatArgs(unsigned numArgs, OutString* out) {
164 if (numArgs > stackDepth()) {
165 _ASSERTE(!"Underflow error");
166 *out << "<UNDERFLOW ERROR>";
170 for(unsigned i = 0; i < numArgs; i++) {
171 if (i != 0) *out << ", ";
172 *out << stackCur[i].val.val();
178 /***************************************************************************/
179 void ILFormatter::formatInstrArgs(OpInfo op, OpArgsVal arg, OutString* out, size_t curILOffset) {
181 MDUTF8CSTR typeName=0;
183 switch(op.getArgsInfo() & PrimaryMask) {
191 out->hex(arg.i, 0, OutString::put0x);
196 case InlineBrTarget: {
197 _ASSERTE(curILOffset != INVALID_IL_OFFSET);
198 size_t target = curILOffset + arg.i;
199 setTarget(target, stackDepth());
200 *out << "IL_"; out->hex(static_cast<unsigned __int64>(target), 4, OutString::zeroFill);
203 out->hex(arg.i, 0, OutString::put0x);
209 hr = meta->GetUserString(arg.i, str, 80, &numChars);
210 _ASSERTE(SUCCEEDED(hr));
213 wcscpy_s(&str[79], 4, W("..."));
219 else if (*ptr == '"')
221 else if (*ptr < 0x20 || * ptr >= 0x80) {
223 out->hex(*ptr, 4, OutString::zeroFill);
234 // Get the typeName if possible
235 mdToken mdType = mdTypeDefNil;
236 if (TypeFromToken(arg.i) == mdtMethodDef)
237 hr = meta->GetMethodProps(mdMethodDef(arg.i), &mdType, 0, 0, 0, 0, 0, 0, 0, 0);
238 else if (TypeFromToken(arg.i) == mdtMemberRef)
239 hr = meta->GetMemberRefProps(mdMemberRef(arg.i), &mdType, 0, 0, 0, 0, 0);
240 else if (TypeFromToken(arg.i) == mdtFieldDef)
241 hr = meta->GetFieldProps(mdMethodDef(arg.i), &mdType, 0, 0, 0, 0, 0, 0, 0, 0, 0);
242 if (SUCCEEDED(hr) && mdType != mdTypeDefNil) {
243 hr = meta->GetNameFromToken(mdType, &typeName);
248 // FIX handle case if (TypeFromToken(arg.i) == mdtTypeSpec)
250 hr = meta->GetNameFromToken(arg.i, &name);
253 const char* lastDot = strrchr(typeName, '.');
254 if (lastDot) typeName = lastDot + 1;
255 *out << typeName << "::";
261 out->hex(arg.i, 0, OutString::put0x);
267 out->hex(arg.i, 0, OutString::put0x);
271 _ASSERTE(curILOffset != INVALID_IL_OFFSET);
272 unsigned count = arg.switch_.count;
274 for (i = 0; i < count; i++) {
275 size_t target = curILOffset + GET_UNALIGNED_VAL32(&arg.switch_.targets[i]);
276 setTarget(target, stackDepth()-1);
277 *out << "IL_"; out->hex(static_cast<unsigned __int64>(target), 4, OutString::zeroFill);
283 unsigned count = arg.phi.count;
285 for (i = 0; i < count; i++) {
286 *out << GET_UNALIGNED_VAL32(&arg.phi.vars[i]);
292 _ASSERTE(!"BadType");
297 #pragma warning(push)
298 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
300 /***************************************************************************/
301 const BYTE* ILFormatter::formatStatement(const BYTE* instrPtr, OutString* out) {
305 OutString *lhs, *rhs, *idx;
309 // set stack as it would be if it was begin jumped to
310 setStackAsTarget(instrPtr - start);
312 while(instrPtr < limit) {
314 instrPtr = op.fetch(instrPtr, &inlineArg);
316 switch(op.getOpcode()) {
320 // for now just skip these
332 inlineArg.i = op.getOpcode() - CEE_LDARG_0;
340 result << name << inlineArg.i;
344 pushAndClear(&result, prec); // also clears result!
356 inlineArg.i = op.getOpcode() - CEE_LDLOC_0;
370 result << name << inlineArg.i << " = " << lhs->val();
373 *out << result.val() << '\n';
374 // if flow of control does not fall through,
375 // assume the stack is empty
376 if (op.getFlow() == FLOW_BRANCH || op.getFlow() == FLOW_RETURN ||
377 op.getFlow() == FLOW_THROW) {
386 inlineArg.i = op.getOpcode() - CEE_STLOC_0;
405 inlineArg.i = op.getOpcode() - CEE_LDC_I4_0;
409 result << inlineArg.i;
414 result.hex(inlineArg.i8);
420 result << inlineArg.r;
430 formatInstrArgs(op, inlineArg, &result);
436 name = "=="; prec = 0x40; goto DO_BR_BINOP;
439 name = ">="; prec = 0x40; goto DO_BR_BINOP;
442 name = ">=un"; prec = 0x40; goto DO_BR_BINOP;
446 name = ">"; prec = 0x40; goto DO_BR_BINOP;
449 name = ">un"; prec = 0x40; goto DO_BR_BINOP;
452 name = "<="; prec = 0x40; goto DO_BR_BINOP;
455 name = "<=un"; prec = 0x40; goto DO_BR_BINOP;
458 name = "<"; prec = 0x40; goto DO_BR_BINOP;
461 name = "<un"; prec = 0x40; goto DO_BR_BINOP;
464 name = "!=un"; prec = 0x40; goto DO_BR_BINOP;
468 result << "if (" << lhs->val() << ' ' << name << ' ' << rhs->val() << ") ";
473 while (stackDepth() > 0) {
475 *lhs << '\n' << result; // put the result in front of anything else
482 size_t target = (instrPtr - start) + inlineArg.i;
483 setTarget(target, stackDepth());
484 result << "goto IL_"; result.hex(static_cast<unsigned __int64>(target), 4, OutString::zeroFill);
496 result << "if (" << name << lhs->val() << ") ";
500 name = "|"; prec = 0x20; goto DO_BINOP;
502 name = "^"; prec = 0x20; goto DO_BINOP;
504 name = "&"; prec = 0x30; goto DO_BINOP;
506 name = "<<"; prec = 0x50; goto DO_BINOP;
508 name = ">>"; prec = 0x50; goto DO_BINOP;
510 name = ">>un"; prec = 0x50; goto DO_BINOP;
512 name = "=="; prec = 0x40; goto DO_BINOP;
514 name = ">"; prec = 0x40; goto DO_BINOP;
516 name = ">un"; prec = 0x40; goto DO_BINOP;
518 name = "<"; prec = 0x40; goto DO_BINOP;
520 name = "<un"; prec = 0x40; goto DO_BINOP;
522 name = "+"; prec = 0x60; goto DO_BINOP;
524 name = "+ovf"; prec = 0x60; goto DO_BINOP;
526 name = "+ovf.un";prec = 0x60; goto DO_BINOP;
528 name = "-"; prec = 0x60; goto DO_BINOP;
530 name = "-ovf"; prec = 0x60; goto DO_BINOP;
532 name = "-ovf.un";prec = 0x60; goto DO_BINOP;
534 name = "*"; prec = 0x70; goto DO_BINOP;
536 name = "*ovf"; prec = 0x70; goto DO_BINOP;
538 name = "*ovf.un";prec = 0x70; goto DO_BINOP;
540 name = "/"; prec = 0x70; goto DO_BINOP;
542 name = "/un"; prec = 0x70; goto DO_BINOP;
544 name = "%"; prec = 0x70; goto DO_BINOP;
546 name = "%un"; prec = 0x70; goto DO_BINOP;
550 result << lhs->val() << ' ' << name << ' ' << rhs->val();
554 name = "~"; prec = 0x80; goto DO_UNOP;
556 name = "-"; prec = 0x80; goto DO_UNOP;
559 result << name << lhs->val();
563 _ASSERTE(stackDepth() <= 1);
565 if (stackDepth() > 0) {
567 result << ' ' << lhs->val();
579 result << lhs->val();
580 prec = 0x1000; // spillstack makes them temps, so they have high prec
585 goto DO_LDFLD_LDFLDA;
591 result << name << lhs->val() << '.';
592 formatInstrArgs(op, inlineArg, &result);
597 goto DO_LDSFLD_LDSFLDA;
603 formatInstrArgs(op, inlineArg, &result);
609 result << lhs->val() << '.';
610 formatInstrArgs(op, inlineArg, &result);
611 result << " = " << rhs->val();
616 formatInstrArgs(op, inlineArg, &result);
617 result << " = " << rhs->val();
622 result << "CALLI<" << lhs->val() << '>';
630 formatInstrArgs(op, inlineArg, &result);
633 // Get the signature stuff
637 if (TypeFromToken(inlineArg.i) == mdtMethodDef)
638 hr = meta->GetMethodProps(mdMethodDef(inlineArg.i), 0, 0, 0, 0, 0, &sig, &cSig, 0, 0);
639 else if (TypeFromToken(inlineArg.i) == mdtMemberRef)
640 hr = meta->GetMemberRefProps(mdMemberRef(inlineArg.i), 0, 0, 0, 0, &sig, &cSig);
642 hr = meta->GetSigFromToken(mdSignature(inlineArg.i), &sig, &cSig);
643 _ASSERTE(SUCCEEDED(hr));
644 unsigned callConv = CorSigUncompressData(sig);
645 unsigned hasThis = callConv & IMAGE_CEE_CS_CALLCONV_HASTHIS;
646 if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
648 CorSigUncompressData(sig);
650 unsigned numArgs = CorSigUncompressData(sig);
651 while(*sig == ELEMENT_TYPE_CMOD_REQD || *sig == ELEMENT_TYPE_CMOD_OPT) {
653 CorSigUncompressToken(sig);
656 formatArgs(numArgs, &result);
657 if (hasThis && op.getOpcode() != CEE_NEWOBJ) {
660 result << '.' << lhs->val();
663 if (op.getOpcode() == CEE_NEWOBJ || *sig != ELEMENT_TYPE_VOID)
679 result << lhs->val() << '[' << rhs->val() << ']';
694 result << lhs->val() << '[' << idx->val() << "] = " << rhs->val();
697 case CEE_LDIND_I1: name = "I1"; goto DO_LDIND;
698 case CEE_LDIND_I2: name = "I2"; goto DO_LDIND;
699 case CEE_LDIND_I4: name = "I4"; goto DO_LDIND;
700 case CEE_LDIND_I8: name = "I8"; goto DO_LDIND;
701 case CEE_LDIND_I: name = "I"; goto DO_LDIND;
702 case CEE_LDIND_R4: name = "R4"; goto DO_LDIND;
703 case CEE_LDIND_R8: name = "R8"; goto DO_LDIND;
704 case CEE_LDIND_U1: name = "U1"; goto DO_LDIND;
705 case CEE_LDIND_U2: name = "U2"; goto DO_LDIND;
706 case CEE_LDIND_REF: name = "REF";goto DO_LDIND;
710 result << name << "(*" << lhs->val() << ')';
713 case CEE_STIND_I1: name = "I1"; goto DO_STIND;
714 case CEE_STIND_I2: name = "I2"; goto DO_STIND;
715 case CEE_STIND_I4: name = "I4"; goto DO_STIND;
716 case CEE_STIND_I8: name = "I8"; goto DO_STIND;
717 case CEE_STIND_REF: name = "REF";goto DO_STIND;
718 case CEE_STIND_R4: name = "R4"; goto DO_STIND;
719 case CEE_STIND_R8: name = "R8"; goto DO_STIND;
723 result << '*' << lhs->val() << " = " << name << '(' << rhs->val() << ')';
762 case CEE_CONV_OVF_I_UN:
763 case CEE_CONV_OVF_I1_UN:
764 case CEE_CONV_OVF_I2_UN:
765 case CEE_CONV_OVF_I4_UN:
766 case CEE_CONV_OVF_I8_UN:
767 case CEE_CONV_OVF_U_UN:
768 case CEE_CONV_OVF_U1_UN:
769 case CEE_CONV_OVF_U2_UN:
770 case CEE_CONV_OVF_U4_UN:
771 case CEE_CONV_OVF_U8_UN:
772 case CEE_CONV_OVF_I1:
773 case CEE_CONV_OVF_I2:
774 case CEE_CONV_OVF_I4:
775 case CEE_CONV_OVF_I8:
776 case CEE_CONV_OVF_U1:
777 case CEE_CONV_OVF_U2:
778 case CEE_CONV_OVF_U4:
779 case CEE_CONV_OVF_U8:
797 result << op.getName();
798 if (op.getArgsInfo() != InlineNone) {
800 formatInstrArgs(op, inlineArg, &result, instrPtr-start);
804 _ASSERTE(op.getNumPop() >= 0);
805 if (op.getNumPop() > 0)
806 formatArgs(op.getNumPop(), &result);
809 _ASSERTE(op.getNumPush() == 0 || op.getNumPush() == 1);
810 if (op.getNumPush() > 0)