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.
4
5
6
7#include "common.h"
8#include "sigformat.h"
9#include "typedesc.h"
10
11SigFormat::SigFormat()
12{
13 WRAPPER_NO_CONTRACT; // THROWS;GC_TRIGGERS;INJECT_FAULT(ThrowOM)
14 _size = SIG_INC;
15 _pos = 0;
16 _fmtSig = new char[_size];
17}
18
19SigFormat::SigFormat(MetaSig &metaSig, LPCUTF8 szMemberName, LPCUTF8 szClassName, LPCUTF8 szNameSpace)
20{
21 WRAPPER_NO_CONTRACT;
22 FormatSig(metaSig, szMemberName, szClassName, szNameSpace);
23}
24
25
26// SigFormat::SigFormat()
27// This constructor will create the string representation of a
28// method.
29SigFormat::SigFormat(MethodDesc* pMeth, TypeHandle owner, BOOL fIgnoreMethodName)
30{
31 CONTRACTL
32 {
33 THROWS;
34 GC_TRIGGERS;
35 INJECT_FAULT(COMPlusThrowOM());
36 }
37 CONTRACTL_END
38
39 // Explicitly use MethodDesc::LoadMethodInstantiation so that we can succesfully format
40 // non-typical generic method definitions.
41 MetaSig sig(pMeth, pMeth->GetExactClassInstantiation(owner), pMeth->LoadMethodInstantiation());
42
43 if (fIgnoreMethodName)
44 {
45 FormatSig(sig, NULL);
46 }
47 else
48 {
49 FormatSig(sig, pMeth->GetName());
50 }
51}
52
53
54SigFormat::~SigFormat()
55{
56 LIMITED_METHOD_CONTRACT;
57
58 if (_fmtSig)
59 delete [] _fmtSig;
60}
61
62const char * SigFormat::GetCString()
63{
64 LIMITED_METHOD_CONTRACT;
65 return _fmtSig;
66}
67
68const char * SigFormat::GetCStringParmsOnly()
69{
70 LIMITED_METHOD_CONTRACT;
71 // _fmtSig looks like: "void Put (byte[], int, int)".
72 // Skip to the '('.
73 int skip;
74 for(skip=0; _fmtSig[skip]!='('; skip++)
75 ;
76 return _fmtSig + skip;
77}
78
79void SigFormat::AddString(LPCUTF8 s)
80{
81 CONTRACTL
82 {
83 THROWS;
84 GC_TRIGGERS;
85 INJECT_FAULT(COMPlusThrowOM());
86 }
87 CONTRACTL_END
88
89 size_t len = strlen(s);
90 // Allocate on overflow
91 size_t requiredSize = _pos + len + 1;
92
93 if (requiredSize <= _pos) { // check for integer overflow in previous calc
94 #ifndef DACCESS_COMPILE
95 COMPlusThrowOM();
96 #else
97 DacError(E_OUTOFMEMORY);
98 #endif
99 }
100
101 if (requiredSize > _size) {
102 size_t newSize = (_size+SIG_INC > requiredSize) ? _size+SIG_INC : requiredSize+SIG_INC;
103 char* temp = new char[newSize];
104 memcpy(temp,_fmtSig,_size);
105 delete [] _fmtSig;
106 _fmtSig = temp;
107 _size=newSize;
108 }
109 strcpy_s(&_fmtSig[_pos],_size - (&_fmtSig[_pos] - _fmtSig), s);
110 _pos += len;
111}
112
113
114//------------------------------------------------------------------------
115// Replacement for SigFormat::AddType that avoids class loading
116// and copes with formal type parameters
117//------------------------------------------------------------------------
118void SigFormat::AddTypeString(Module* pModule, SigPointer sig, const SigTypeContext *pTypeContext)
119{
120 CONTRACTL
121 {
122 THROWS;
123 GC_TRIGGERS;
124 INJECT_FAULT(COMPlusThrowOM());
125 }
126 CONTRACTL_END
127
128 LPCUTF8 szcName;
129 LPCUTF8 szcNameSpace;
130 /*
131 ULONG cArgs;
132 VOID *pEnum;
133 ULONG i;
134 */
135
136 CorElementType type;
137 IfFailThrow(sig.GetElemType(&type));
138
139 // Format the output
140 switch (type)
141 {
142// @Todo: Should these be ilasm-style types?
143 case ELEMENT_TYPE_VOID: AddString("Void"); break;
144 case ELEMENT_TYPE_BOOLEAN: AddString("Boolean"); break;
145 case ELEMENT_TYPE_I1: AddString("SByte"); break;
146 case ELEMENT_TYPE_U1: AddString("Byte"); break;
147 case ELEMENT_TYPE_I2: AddString("Int16"); break;
148 case ELEMENT_TYPE_U2: AddString("UInt16"); break;
149 case ELEMENT_TYPE_CHAR: AddString("Char"); break;
150 case ELEMENT_TYPE_I: AddString("IntPtr"); break;
151 case ELEMENT_TYPE_U: AddString("UIntPtr"); break;
152 case ELEMENT_TYPE_I4: AddString("Int32"); break;
153 case ELEMENT_TYPE_U4: AddString("UInt32"); break;
154 case ELEMENT_TYPE_I8: AddString("Int64"); break;
155 case ELEMENT_TYPE_U8: AddString("UInt64"); break;
156 case ELEMENT_TYPE_R4: AddString("Single"); break;
157 case ELEMENT_TYPE_R8: AddString("Double"); break;
158 case ELEMENT_TYPE_OBJECT: AddString(g_ObjectClassName); break;
159 case ELEMENT_TYPE_STRING: AddString(g_StringClassName); break;
160
161 // For Value Classes we fall through unless the pVMC is an Array Class,
162 // If its an array class we need to get the name of the underlying type from
163 // it.
164 case ELEMENT_TYPE_VALUETYPE:
165 case ELEMENT_TYPE_CLASS:
166 {
167 IMDInternalImport *pInternalImport = pModule->GetMDImport();
168 mdToken token;
169 IfFailThrow(sig.GetToken(&token));
170
171 if (TypeFromToken(token) == mdtTypeDef)
172 {
173 IfFailThrow(pInternalImport->GetNameOfTypeDef(token, &szcName, &szcNameSpace));
174 }
175 else if (TypeFromToken(token) == mdtTypeRef)
176 {
177 IfFailThrow(pInternalImport->GetNameOfTypeRef(token, &szcNameSpace, &szcName));
178 }
179 else
180 break;
181
182 if (*szcNameSpace)
183 {
184 AddString(szcNameSpace);
185 AddString(".");
186 }
187 AddString(szcName);
188 break;
189 }
190 case ELEMENT_TYPE_INTERNAL:
191 {
192 TypeHandle hType;
193
194 // this check is not functional in DAC and provides no security against a malicious dump
195 // the DAC is prepared to receive an invalid type handle
196#ifndef DACCESS_COMPILE
197 if (pModule->IsSigInIL(sig.GetPtr()))
198 THROW_BAD_FORMAT(BFA_BAD_COMPLUS_SIG, pModule);
199#endif
200
201 CorSigUncompressPointer(sig.GetPtr(), (void**)&hType);
202 _ASSERTE(!hType.IsNull());
203 MethodTable *pMT = hType.GetMethodTable();
204 _ASSERTE(pMT);
205 mdToken token = pMT->GetCl();
206 IfFailThrow(pMT->GetMDImport()->GetNameOfTypeDef(token, &szcName, &szcNameSpace));
207 if (*szcNameSpace)
208 {
209 AddString(szcNameSpace);
210 AddString(".");
211 }
212 AddString(szcName);
213 break;
214 }
215 case ELEMENT_TYPE_TYPEDBYREF:
216 {
217 AddString("TypedReference");
218 break;
219 }
220
221 case ELEMENT_TYPE_BYREF:
222 {
223 AddTypeString(pModule, sig, pTypeContext);
224 AddString(" ByRef");
225 }
226 break;
227
228 case ELEMENT_TYPE_MVAR :
229 {
230 DWORD ix;
231 IfFailThrow(sig.GetData(&ix));
232 if (pTypeContext && !pTypeContext->m_methodInst.IsEmpty() && ix >= 0 && ix < pTypeContext->m_methodInst.GetNumArgs())
233 {
234 AddType(pTypeContext->m_methodInst[ix]);
235 }
236 else
237 {
238 char smallbuf[20];
239 sprintf_s(smallbuf, COUNTOF(smallbuf), "!!%d", ix);
240 AddString(smallbuf);
241 }
242 }
243 break;
244
245 case ELEMENT_TYPE_VAR :
246 {
247 DWORD ix;
248 IfFailThrow(sig.GetData(&ix));
249
250 if (pTypeContext && !pTypeContext->m_classInst.IsEmpty() && ix >= 0 && ix < pTypeContext->m_classInst.GetNumArgs())
251 {
252 AddType(pTypeContext->m_classInst[ix]);
253 }
254 else
255 {
256 char smallbuf[20];
257 sprintf_s(smallbuf, COUNTOF(smallbuf), "!%d", ix);
258 AddString(smallbuf);
259 }
260 }
261 break;
262
263 case ELEMENT_TYPE_GENERICINST :
264 {
265 AddTypeString(pModule, sig, pTypeContext);
266
267 IfFailThrow(sig.SkipExactlyOne());
268 DWORD n;
269 IfFailThrow(sig.GetData(&n));
270
271 AddString("<");
272 for (DWORD i = 0; i < n; i++)
273 {
274 if (i > 0)
275 AddString(",");
276 AddTypeString(pModule,sig, pTypeContext);
277 IfFailThrow(sig.SkipExactlyOne());
278 }
279 AddString(">");
280
281 break;
282 }
283
284 case ELEMENT_TYPE_SZARRAY: // Single Dim, Zero
285 case ELEMENT_TYPE_ARRAY: // General Array
286 {
287 AddTypeString(pModule, sig, pTypeContext);
288 IfFailThrow(sig.SkipExactlyOne());
289 if (type == ELEMENT_TYPE_ARRAY)
290 {
291 AddString("[");
292 ULONG len;
293 IfFailThrow(sig.GetData(&len));
294
295 for (ULONG i=1;i<len;i++)
296 AddString(",");
297
298 AddString("]");
299 }
300 else
301 {
302 AddString("[]");
303 }
304 }
305 break;
306
307 case ELEMENT_TYPE_PTR:
308 {
309 // This will pop up on methods that take a pointer to a block of unmanaged memory.
310 AddTypeString(pModule, sig, pTypeContext);
311 AddString("*");
312 break;
313 }
314
315 case ELEMENT_TYPE_FNPTR:
316 {
317 DWORD callConv;
318 IfFailThrow(sig.GetData(&callConv));
319
320 ULONG cArgs;
321 IfFailThrow(sig.GetData(&cArgs));
322
323 AddTypeString(pModule, sig, pTypeContext);
324 IfFailThrow(sig.SkipExactlyOne());
325 AddString(" (");
326 ULONG i;
327 for (i = 0; i < cArgs; i++) {
328 AddTypeString(pModule, sig, pTypeContext);
329 IfFailThrow(sig.SkipExactlyOne());
330 if (i != (cArgs - 1))
331 AddString(", ");
332 }
333 if ((callConv & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_CS_CALLCONV_VARARG)
334 {
335 if (cArgs)
336 AddString(", ");
337 AddString("...");
338 }
339 AddString(")");
340 break;
341 }
342
343 default:
344 AddString("**UNKNOWN TYPE**");
345
346 }
347}
348
349void SigFormat::FormatSig(MetaSig &sig, LPCUTF8 szMemberName, LPCUTF8 szClassName, LPCUTF8 szNameSpace)
350{
351 CONTRACTL
352 {
353 THROWS;
354 GC_TRIGGERS;
355 INJECT_FAULT(COMPlusThrowOM());
356 }
357 CONTRACTL_END
358
359 UINT cArgs;
360
361 _size = SIG_INC;
362 _pos = 0;
363 _fmtSig = new char[_size];
364
365 AddTypeString(sig.GetModule(), sig.GetReturnProps(), sig.GetSigTypeContext());
366
367 AddString(" ");
368 if (szNameSpace != NULL)
369 {
370 AddString(szNameSpace);
371 AddString(".");
372 }
373 if (szClassName != NULL)
374 {
375 AddString(szClassName);
376 AddString(".");
377 }
378 if (szMemberName != NULL)
379 {
380 AddString(szMemberName);
381 }
382
383 cArgs = sig.NumFixedArgs();
384 sig.Reset();
385
386 AddString("(");
387
388 // Loop through all of the args
389 for (UINT i=0;i<cArgs;i++) {
390 sig.NextArg();
391 AddTypeString(sig.GetModule(), sig.GetArgProps(), sig.GetSigTypeContext());
392 if (i != cArgs-1)
393 AddString(", ");
394 }
395
396 // Display vararg signature at end
397 if (sig.IsVarArg())
398 {
399 if (cArgs)
400 AddString(", ");
401 AddString("...");
402 }
403
404 AddString(")");
405}
406
407void SigFormat::AddType(TypeHandle th)
408{
409 CONTRACTL
410 {
411 THROWS;
412 GC_TRIGGERS;
413 INJECT_FAULT(COMPlusThrowOM());
414 }
415 CONTRACTL_END
416
417 LPCUTF8 szcName;
418 LPCUTF8 szcNameSpace;
419 ULONG cArgs;
420 ULONG i;
421
422 if (th.IsNull())
423 {
424 AddString("**UNKNOWN TYPE**");
425 return;
426 }
427
428 CorElementType type = th.GetSignatureCorElementType();
429
430 // Format the output
431 switch (type)
432 {
433// <TODO>@Todo: Should these be ilasm-style types?</TODO>
434 case ELEMENT_TYPE_VOID: AddString("Void"); break;
435 case ELEMENT_TYPE_BOOLEAN: AddString("Boolean"); break;
436 case ELEMENT_TYPE_I1: AddString("SByte"); break;
437 case ELEMENT_TYPE_U1: AddString("Byte"); break;
438 case ELEMENT_TYPE_I2: AddString("Int16"); break;
439 case ELEMENT_TYPE_U2: AddString("UInt16"); break;
440 case ELEMENT_TYPE_CHAR: AddString("Char"); break;
441 case ELEMENT_TYPE_I: AddString("IntPtr"); break;
442 case ELEMENT_TYPE_U: AddString("UIntPtr"); break;
443 case ELEMENT_TYPE_I4: AddString("Int32"); break;
444 case ELEMENT_TYPE_U4: AddString("UInt32"); break;
445 case ELEMENT_TYPE_I8: AddString("Int64"); break;
446 case ELEMENT_TYPE_U8: AddString("UInt64"); break;
447 case ELEMENT_TYPE_R4: AddString("Single"); break;
448 case ELEMENT_TYPE_R8: AddString("Double"); break;
449 case ELEMENT_TYPE_OBJECT: AddString(g_ObjectClassName); break;
450 case ELEMENT_TYPE_STRING: AddString(g_StringClassName); break;
451
452 // For Value Classes we fall through unless the pVMC is an Array Class,
453 // If its an array class we need to get the name of the underlying type from
454 // it.
455 case ELEMENT_TYPE_VALUETYPE:
456 case ELEMENT_TYPE_CLASS:
457 {
458 if (FAILED(th.GetMethodTable()->GetMDImport()->GetNameOfTypeDef(th.GetCl(), &szcName, &szcNameSpace)))
459 {
460 szcName = szcNameSpace = "Invalid TypeDef record";
461 }
462
463 if (*szcNameSpace != 0)
464 {
465 AddString(szcNameSpace);
466 AddString(".");
467 }
468 AddString(szcName);
469
470 if (th.HasInstantiation())
471 {
472 Instantiation inst = th.GetInstantiation();
473
474 if(!inst.IsEmpty())
475 {
476 AddString("<");
477 for (DWORD j = 0; j < th.GetNumGenericArgs(); j++)
478 {
479 if (j > 0)
480 AddString(",");
481
482 AddType(inst[j]);
483 }
484 AddString(">");
485 }
486 }
487
488 break;
489 }
490 case ELEMENT_TYPE_TYPEDBYREF:
491 {
492 AddString("TypedReference");
493 break;
494 }
495
496 case ELEMENT_TYPE_BYREF:
497 {
498 TypeHandle h = th.AsTypeDesc()->GetTypeParam();
499 AddType(h);
500 AddString(" ByRef");
501 }
502 break;
503
504 case ELEMENT_TYPE_SZARRAY: // Single Dim, Zero
505 case ELEMENT_TYPE_ARRAY: // General Array
506 {
507 ArrayTypeDesc* aTD = th.AsArray();
508 AddType(aTD->GetArrayElementTypeHandle());
509
510 if (type == ELEMENT_TYPE_ARRAY)
511 {
512 AddString("[");
513 int len = aTD->GetRank();
514
515 for (int j=0;j<len-1;j++)
516
517 AddString(",");
518 AddString("]");
519 }
520 else
521 {
522 AddString("[]");
523 }
524 }
525 break;
526
527 case ELEMENT_TYPE_PTR:
528 {
529 // This will pop up on methods that take a pointer to a block of unmanaged memory.
530 TypeHandle h = th.AsTypeDesc()->GetTypeParam();
531 AddType(h);
532 AddString("*");
533 break;
534 }
535 case ELEMENT_TYPE_FNPTR:
536 {
537 FnPtrTypeDesc* pTD = th.AsFnPtrType();
538
539 TypeHandle *pRetAndArgTypes = pTD->GetRetAndArgTypes();
540 AddType(pRetAndArgTypes[0]);
541 AddString(" (");
542
543 cArgs = pTD->GetNumArgs();
544
545 for (i = 0; i < cArgs; i++)
546 {
547 AddType(pRetAndArgTypes[i+1]);
548
549 if (i != (cArgs - 1))
550 AddString(", ");
551 }
552 if ((pTD->GetCallConv() & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_CS_CALLCONV_VARARG)
553 {
554 AddString(", ");
555
556 AddString("...");
557 }
558 AddString(")");
559
560 break;
561 }
562
563 case ELEMENT_TYPE_VAR:
564 case ELEMENT_TYPE_MVAR:
565 {
566 StackScratchBuffer scratch;
567 StackSString name;
568 th.GetName(name);
569
570 AddString(name.GetANSI(scratch));
571
572 break;
573 }
574
575 default:
576 AddString("**UNKNOWN TYPE**");
577 }
578}
579
580//
581// Legacy debug-only string formatting pretty printer
582//
583
584#ifdef _DEBUG
585#ifndef DACCESS_COMPILE
586
587#include <formattype.h>
588
589/*******************************************************************/
590const char* FormatSigHelper(MethodDesc* pMD, CQuickBytes *out, LoaderHeap *pHeap, AllocMemTracker *pamTracker)
591{
592 PCCOR_SIGNATURE pSig;
593 ULONG cSig;
594 const char *ret = NULL;
595
596 pMD->GetSig(&pSig, &cSig);
597
598 if (pSig == NULL)
599 {
600 return "<null>";
601 }
602
603 EX_TRY
604 {
605 const char* sigStr = PrettyPrintSig(pSig, cSig, "*", out, pMD->GetMDImport(), 0);
606
607 S_SIZE_T len = S_SIZE_T(strlen(sigStr))+S_SIZE_T(1);
608 if(len.IsOverflow()) COMPlusThrowHR(COR_E_OVERFLOW);
609
610 char* mem = (char*) pamTracker->Track(pHeap->AllocMem(len));
611
612 if (mem != NULL)
613 {
614 strcpy_s(mem, len.Value(), sigStr);
615 ret = mem;
616 }
617 }
618 EX_CATCH
619 {
620 }
621 EX_END_CATCH(RethrowTerminalExceptions);
622
623 return ret;
624}
625
626/*******************************************************************/
627const char* FormatSig(MethodDesc * pMD, LoaderHeap * pHeap, AllocMemTracker * pamTracker)
628{
629 CONTRACTL
630 {
631 THROWS;
632 GC_NOTRIGGER;
633 }
634 CONTRACTL_END;
635
636 CQuickBytes out;
637 return FormatSigHelper(pMD, &out, pHeap, pamTracker);
638}
639
640/*******************************************************************/
641const char* FormatSig(MethodDesc* pMD, AppDomain *pDomain, AllocMemTracker *pamTracker)
642{
643 CONTRACTL
644 {
645 THROWS;
646 GC_NOTRIGGER;
647 }
648 CONTRACTL_END;
649
650 return FormatSig(pMD,pDomain->GetLowFrequencyHeap(),pamTracker);
651}
652#endif
653#endif
654