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 | |
11 | SigFormat::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 | |
19 | SigFormat::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. |
29 | SigFormat::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 | |
54 | SigFormat::~SigFormat() |
55 | { |
56 | LIMITED_METHOD_CONTRACT; |
57 | |
58 | if (_fmtSig) |
59 | delete [] _fmtSig; |
60 | } |
61 | |
62 | const char * SigFormat::GetCString() |
63 | { |
64 | LIMITED_METHOD_CONTRACT; |
65 | return _fmtSig; |
66 | } |
67 | |
68 | const 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 | |
79 | void 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 | //------------------------------------------------------------------------ |
118 | void 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 | |
349 | void 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 | |
407 | void 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 | /*******************************************************************/ |
590 | const 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 | /*******************************************************************/ |
627 | const 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 | /*******************************************************************/ |
641 | const 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 | |