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// File: compile.cpp
6//
7
8//
9// Support for zap compiler and zap files
10// ===========================================================================
11
12
13
14#include "common.h"
15
16#ifdef FEATURE_PREJIT
17
18#include <corcompile.h>
19
20#include "assemblyspec.hpp"
21
22#include "compile.h"
23#include "excep.h"
24#include "field.h"
25#include "eeconfig.h"
26#include "zapsig.h"
27#include "gcrefmap.h"
28
29
30#include "virtualcallstub.h"
31#include "typeparse.h"
32#include "typestring.h"
33#include "dllimport.h"
34#include "comdelegate.h"
35#include "stringarraylist.h"
36
37#ifdef FEATURE_COMINTEROP
38#include "clrtocomcall.h"
39#include "comtoclrcall.h"
40#include "winrttypenameconverter.h"
41#endif // FEATURE_COMINTEROP
42
43#include "dllimportcallback.h"
44#include "caparser.h"
45#include "sigbuilder.h"
46#include "cgensys.h"
47#include "peimagelayout.inl"
48
49
50#ifdef FEATURE_COMINTEROP
51#include "clrprivbinderwinrt.h"
52#include "winrthelpers.h"
53#endif
54
55#ifdef CROSSGEN_COMPILE
56#include "crossgenroresolvenamespace.h"
57#endif
58
59#ifndef NO_NGENPDB
60#include <cvinfo.h>
61#endif
62
63#ifdef FEATURE_PERFMAP
64#include "perfmap.h"
65#endif
66
67#include "argdestination.h"
68
69#include "versionresilienthashcode.h"
70#include "inlinetracking.h"
71#include "jithost.h"
72
73#ifdef CROSSGEN_COMPILE
74CompilationDomain * theDomain;
75#endif
76
77VerboseLevel g_CorCompileVerboseLevel = CORCOMPILE_NO_LOG;
78
79//
80// CEECompileInfo implements most of ICorCompileInfo
81//
82
83HRESULT CEECompileInfo::Startup( BOOL fForceDebug,
84 BOOL fForceProfiling,
85 BOOL fForceInstrument)
86{
87 SystemDomain::SetCompilationOverrides(fForceDebug,
88 fForceProfiling,
89 fForceInstrument);
90
91 HRESULT hr = S_OK;
92
93 m_fCachingOfInliningHintsEnabled = TRUE;
94
95 _ASSERTE(!g_fEEStarted && !g_fEEInit && "You cannot run the EE inside an NGEN compilation process");
96
97 if (!g_fEEStarted && !g_fEEInit)
98 {
99#ifdef CROSSGEN_COMPILE
100 GetSystemInfo(&g_SystemInfo);
101
102 theDomain = new CompilationDomain(fForceDebug,
103 fForceProfiling,
104 fForceInstrument);
105#endif
106
107 // When NGEN'ing this call may execute EE code, e.g. the managed code to set up
108 // the SharedDomain.
109 hr = InitializeEE(COINITEE_DEFAULT);
110 }
111
112 //
113 // JIT interface expects to be called with
114 // preemptive GC enabled
115 //
116 if (SUCCEEDED(hr)) {
117#ifdef _DEBUG
118 Thread *pThread = GetThread();
119 _ASSERTE(pThread);
120#endif
121
122 GCX_PREEMP_NO_DTOR();
123 }
124
125 return hr;
126}
127
128HRESULT CEECompileInfo::CreateDomain(ICorCompilationDomain **ppDomain,
129 IMetaDataAssemblyEmit *pEmitter,
130 BOOL fForceDebug,
131 BOOL fForceProfiling,
132 BOOL fForceInstrument)
133{
134 STANDARD_VM_CONTRACT;
135
136 COOPERATIVE_TRANSITION_BEGIN();
137
138 CompilationDomain * pCompilationDomain = theDomain;
139
140 {
141 SystemDomain::LockHolder lh;
142 pCompilationDomain->Init();
143 }
144
145 if (pEmitter)
146 pCompilationDomain->SetDependencyEmitter(pEmitter);
147
148
149#ifdef DEBUGGING_SUPPORTED
150 // Notify the debugger here, before the thread transitions into the
151 // AD to finish the setup, and before any assemblies are loaded into it.
152 SystemDomain::PublishAppDomainAndInformDebugger(pCompilationDomain);
153#endif // DEBUGGING_SUPPORTED
154
155 pCompilationDomain->LoadSystemAssemblies();
156
157 pCompilationDomain->SetupSharedStatics();
158
159 *ppDomain = static_cast<ICorCompilationDomain*>(pCompilationDomain);
160
161 {
162 GCX_COOP();
163
164 ENTER_DOMAIN_PTR(pCompilationDomain,ADV_COMPILATION)
165 {
166 pCompilationDomain->CreateFusionContext();
167
168 pCompilationDomain->SetFriendlyName(W("Compilation Domain"));
169 SystemDomain::System()->LoadDomain(pCompilationDomain);
170 }
171 END_DOMAIN_TRANSITION;
172 }
173
174 COOPERATIVE_TRANSITION_END();
175
176 return S_OK;
177}
178
179
180HRESULT CEECompileInfo::DestroyDomain(ICorCompilationDomain *pDomain)
181{
182 STANDARD_VM_CONTRACT;
183
184#ifndef CROSSGEN_COMPILE
185 COOPERATIVE_TRANSITION_BEGIN();
186
187 GCX_COOP();
188
189 CompilationDomain *pCompilationDomain = (CompilationDomain *) pDomain;
190
191 // DDB 175659: Make sure that canCallNeedsRestore() returns FALSE during compilation
192 // domain shutdown.
193 pCompilationDomain->setCannotCallNeedsRestore();
194
195 pCompilationDomain->Unload(TRUE);
196
197 COOPERATIVE_TRANSITION_END();
198#endif
199
200 return S_OK;
201}
202
203HRESULT MakeCrossDomainCallbackWorker(
204 CROSS_DOMAIN_CALLBACK pfnCallback,
205 LPVOID pArgs)
206{
207 STATIC_CONTRACT_MODE_COOPERATIVE;
208 STATIC_CONTRACT_SO_INTOLERANT;
209
210 HRESULT hrRetVal = E_UNEXPECTED;
211 BEGIN_SO_TOLERANT_CODE(GetThread());
212 hrRetVal = pfnCallback(pArgs);
213 END_SO_TOLERANT_CODE;
214 return hrRetVal;
215}
216
217HRESULT CEECompileInfo::MakeCrossDomainCallback(
218 ICorCompilationDomain* pDomain,
219 CROSS_DOMAIN_CALLBACK pfnCallback,
220 LPVOID pArgs)
221{
222 STANDARD_VM_CONTRACT;
223
224 HRESULT hrRetVal = E_UNEXPECTED;
225
226 COOPERATIVE_TRANSITION_BEGIN();
227
228 {
229 // Switch to cooperative mode to switch appdomains
230 GCX_COOP();
231
232 ENTER_DOMAIN_PTR((CompilationDomain*)pDomain,ADV_COMPILATION)
233 {
234 //
235 // Switch to preemptive mode on before calling back into
236 // the zapper
237 //
238
239 GCX_PREEMP();
240
241 hrRetVal = MakeCrossDomainCallbackWorker(pfnCallback, pArgs);
242 }
243 END_DOMAIN_TRANSITION;
244 }
245
246 COOPERATIVE_TRANSITION_END();
247
248 return hrRetVal;
249}
250
251#ifdef TRITON_STRESS_NEED_IMPL
252int LogToSvcLogger(LPCWSTR format, ...)
253{
254 STANDARD_VM_CONTRACT;
255
256 StackSString s;
257
258 va_list args;
259 va_start(args, format);
260 s.VPrintf(format, args);
261 va_end(args);
262
263 GetSvcLogger()->Printf(W("%s"), s.GetUnicode());
264
265 return 0;
266}
267#endif
268
269HRESULT CEECompileInfo::LoadAssemblyByPath(
270 LPCWSTR wzPath,
271
272 // Normally this is FALSE, but crossgen /CreatePDB sets this to TRUE, so it can
273 // explicitly load an NI by path
274 BOOL fExplicitBindToNativeImage,
275
276 CORINFO_ASSEMBLY_HANDLE *pHandle)
277{
278 STANDARD_VM_CONTRACT;
279
280 HRESULT hr = S_OK;
281
282 COOPERATIVE_TRANSITION_BEGIN();
283
284 Assembly * pAssembly;
285 HRESULT hrProcessLibraryBitnessMismatch = S_OK;
286
287 // We don't want to do a LoadFrom, since they do not work with ngen. Instead,
288 // read the metadata from the file and do a bind based on that.
289
290 EX_TRY
291 {
292 // Pre-open the image so we can grab some metadata to help initialize the
293 // binder's AssemblySpec, which we'll use later to load the assembly for real.
294
295 PEImageHolder pImage;
296
297
298 if (pImage == NULL)
299 {
300 pImage = PEImage::OpenImage(
301 wzPath,
302
303 // If we're explicitly binding to an NGEN image, we do not want the cache
304 // this PEImage for use later, as pointers that need fixup
305 // Normal caching is done when we open it "for real" further down when we
306 // call LoadDomainAssembly().
307 fExplicitBindToNativeImage ? MDInternalImport_NoCache : MDInternalImport_Default);
308 }
309
310 if (fExplicitBindToNativeImage && !pImage->HasReadyToRunHeader())
311 {
312 pImage->VerifyIsNIAssembly();
313 }
314 else
315 {
316 pImage->VerifyIsAssembly();
317 }
318
319 // Check to make sure the bitness of the assembly matches the bitness of the process
320 // we will be loading it into and store the result. If a COR_IMAGE_ERROR gets thrown
321 // by LoadAssembly then we can blame it on bitness mismatch. We do the check here
322 // and not in the CATCH to distinguish between the COR_IMAGE_ERROR that can be thrown by
323 // VerifyIsAssembly (not necessarily a bitness mismatch) and that from LoadAssembly
324#ifdef _TARGET_64BIT_
325 if (pImage->Has32BitNTHeaders())
326 {
327 hrProcessLibraryBitnessMismatch = PEFMT_E_32BIT;
328 }
329#else // !_TARGET_64BIT_
330 if (!pImage->Has32BitNTHeaders())
331 {
332 hrProcessLibraryBitnessMismatch = PEFMT_E_64BIT;
333 }
334#endif // !_TARGET_64BIT_
335
336 AssemblySpec spec;
337 spec.InitializeSpec(TokenFromRid(1, mdtAssembly), pImage->GetMDImport(), NULL);
338
339 if (spec.IsMscorlib())
340 {
341 pAssembly = SystemDomain::System()->SystemAssembly();
342 }
343 else
344 {
345 AppDomain * pDomain = AppDomain::GetCurrentDomain();
346
347 PEAssemblyHolder pAssemblyHolder;
348 BOOL isWinRT = FALSE;
349
350#ifdef FEATURE_COMINTEROP
351 isWinRT = spec.IsContentType_WindowsRuntime();
352 if (isWinRT)
353 {
354 LPCSTR szNameSpace;
355 LPCSTR szTypeName;
356 // It does not make sense to pass the file name to recieve fake type name for empty WinMDs, because we would use the name
357 // for binding in next call to BindAssemblySpec which would fail for fake WinRT type name
358 // We will throw/return the error instead and the caller will recognize it and react to it by not creating the ngen image -
359 // see code:Zapper::ComputeDependenciesInCurrentDomain
360 IfFailThrow(::GetFirstWinRTTypeDef(pImage->GetMDImport(), &szNameSpace, &szTypeName, NULL, NULL));
361 spec.SetWindowsRuntimeType(szNameSpace, szTypeName);
362 }
363#endif //FEATURE_COMINTEROP
364
365 // If there is a host binder then use it to bind the assembly.
366 if (isWinRT)
367 {
368 pAssemblyHolder = pDomain->BindAssemblySpec(&spec, TRUE, FALSE);
369 }
370 else
371 {
372 //ExplicitBind
373 CoreBindResult bindResult;
374 spec.SetCodeBase(pImage->GetPath());
375 spec.Bind(
376 pDomain,
377 TRUE, // fThrowOnFileNotFound
378 &bindResult,
379
380 // fNgenExplicitBind: Generally during NGEN compilation, this is
381 // TRUE, meaning "I am NGEN, and I am doing an explicit bind to the IL
382 // image, so don't infer the NI and try to open it, because I already
383 // have it open". But if we're executing crossgen /CreatePDB, this should
384 // be FALSE so that downstream code doesn't assume we're explicitly
385 // trying to bind to an IL image (we're actually explicitly trying to
386 // open an NI).
387 !fExplicitBindToNativeImage,
388
389 // fExplicitBindToNativeImage: Most callers want this FALSE; but crossgen
390 // /CreatePDB explicitly specifies NI names to open, and cannot assume
391 // that IL assemblies will be available.
392 fExplicitBindToNativeImage
393 );
394 pAssemblyHolder = PEAssembly::Open(&bindResult,FALSE);
395 }
396
397 // Now load assembly into domain.
398 DomainAssembly * pDomainAssembly = pDomain->LoadDomainAssembly(&spec, pAssemblyHolder, FILE_LOAD_BEGIN);
399
400 if (spec.CanUseWithBindingCache() && pDomainAssembly->CanUseWithBindingCache())
401 pDomain->AddAssemblyToCache(&spec, pDomainAssembly);
402
403 pAssembly = pDomain->LoadAssembly(&spec, pAssemblyHolder, FILE_LOADED);
404
405 // Add a dependency to the current assembly. This is done to match the behavior
406 // of LoadAssemblyFusion, so that the same native image is generated whether we
407 // ngen install by file name or by assembly name.
408 pDomain->ToCompilationDomain()->AddDependency(&spec, pAssemblyHolder);
409 }
410
411 // Kind of a workaround - if we could have loaded this assembly via normal load,
412
413 *pHandle = CORINFO_ASSEMBLY_HANDLE(pAssembly);
414 }
415 EX_CATCH_HRESULT(hr);
416
417 if ( hrProcessLibraryBitnessMismatch != S_OK && ( hr == COR_E_BADIMAGEFORMAT || hr == HRESULT_FROM_WIN32(ERROR_BAD_EXE_FORMAT) ) )
418 {
419 hr = hrProcessLibraryBitnessMismatch;
420 }
421
422 COOPERATIVE_TRANSITION_END();
423
424 return hr;
425}
426
427
428#ifdef FEATURE_COMINTEROP
429HRESULT CEECompileInfo::LoadTypeRefWinRT(
430 IMDInternalImport *pAssemblyImport,
431 mdTypeRef ref,
432 CORINFO_ASSEMBLY_HANDLE *pHandle)
433{
434 STANDARD_VM_CONTRACT;
435
436 HRESULT hr = S_OK;
437
438 ReleaseHolder<IAssemblyName> pAssemblyName;
439
440 COOPERATIVE_TRANSITION_BEGIN();
441
442 EX_TRY
443 {
444 Assembly *pAssembly;
445
446 mdToken tkResolutionScope;
447 if(FAILED(pAssemblyImport->GetResolutionScopeOfTypeRef(ref, &tkResolutionScope)))
448 hr = S_FALSE;
449 else if(TypeFromToken(tkResolutionScope) == mdtAssemblyRef)
450 {
451 DWORD dwAssemblyRefFlags;
452 IfFailThrow(pAssemblyImport->GetAssemblyRefProps(tkResolutionScope, NULL, NULL,
453 NULL, NULL,
454 NULL, NULL, &dwAssemblyRefFlags));
455 if (IsAfContentType_WindowsRuntime(dwAssemblyRefFlags))
456 {
457 LPCSTR psznamespace;
458 LPCSTR pszname;
459 pAssemblyImport->GetNameOfTypeRef(ref, &psznamespace, &pszname);
460 AssemblySpec spec;
461 spec.InitializeSpec(tkResolutionScope, pAssemblyImport, NULL);
462 spec.SetWindowsRuntimeType(psznamespace, pszname);
463
464 _ASSERTE(spec.HasBindableIdentity());
465
466 pAssembly = spec.LoadAssembly(FILE_LOADED);
467
468 //
469 // Return the module handle
470 //
471
472 *pHandle = CORINFO_ASSEMBLY_HANDLE(pAssembly);
473 }
474 else
475 {
476 hr = S_FALSE;
477 }
478 }
479 else
480 {
481 hr = S_FALSE;
482 }
483 }
484 EX_CATCH_HRESULT(hr);
485
486 COOPERATIVE_TRANSITION_END();
487
488 return hr;
489}
490#endif
491
492BOOL CEECompileInfo::IsInCurrentVersionBubble(CORINFO_MODULE_HANDLE hModule)
493{
494 WRAPPER_NO_CONTRACT;
495
496 return ((Module*)hModule)->IsInCurrentVersionBubble();
497}
498
499HRESULT CEECompileInfo::LoadAssemblyModule(
500 CORINFO_ASSEMBLY_HANDLE assembly,
501 mdFile file,
502 CORINFO_MODULE_HANDLE *pHandle)
503{
504 STANDARD_VM_CONTRACT;
505
506 COOPERATIVE_TRANSITION_BEGIN();
507
508 Assembly *pAssembly = (Assembly*) assembly;
509
510 Module *pModule = pAssembly->GetManifestModule()->LoadModule(GetAppDomain(), file, TRUE)->GetModule();
511
512 //
513 // Return the module handle
514 //
515
516 *pHandle = CORINFO_MODULE_HANDLE(pModule);
517
518 COOPERATIVE_TRANSITION_END();
519
520 return S_OK;
521}
522
523
524BOOL CEECompileInfo::CheckAssemblyZap(
525 CORINFO_ASSEMBLY_HANDLE assembly,
526 __out_ecount_opt(*cAssemblyManifestModulePath)
527 LPWSTR assemblyManifestModulePath,
528 LPDWORD cAssemblyManifestModulePath)
529{
530 STANDARD_VM_CONTRACT;
531
532 BOOL result = FALSE;
533
534 COOPERATIVE_TRANSITION_BEGIN();
535
536 Assembly *pAssembly = (Assembly*) assembly;
537
538 if (pAssembly->GetManifestFile()->HasNativeImage())
539 {
540 PEImage *pImage = pAssembly->GetManifestFile()->GetPersistentNativeImage();
541
542 if (assemblyManifestModulePath != NULL)
543 {
544 DWORD length = pImage->GetPath().GetCount();
545 if (length > *cAssemblyManifestModulePath)
546 {
547 length = *cAssemblyManifestModulePath - 1;
548 wcsncpy_s(assemblyManifestModulePath, *cAssemblyManifestModulePath, pImage->GetPath(), length);
549 assemblyManifestModulePath[length] = 0;
550 }
551 else
552 wcscpy_s(assemblyManifestModulePath, *cAssemblyManifestModulePath, pImage->GetPath());
553 }
554
555 result = TRUE;
556 }
557
558 COOPERATIVE_TRANSITION_END();
559
560 return result;
561}
562
563HRESULT CEECompileInfo::SetCompilationTarget(CORINFO_ASSEMBLY_HANDLE assembly,
564 CORINFO_MODULE_HANDLE module)
565{
566 STANDARD_VM_CONTRACT;
567
568 Assembly *pAssembly = (Assembly *) assembly;
569 Module *pModule = (Module *) module;
570
571 CompilationDomain *pDomain = (CompilationDomain *) GetAppDomain();
572 pDomain->SetTarget(pAssembly, pModule);
573
574 if (!pAssembly->IsSystem())
575 {
576 // It is possible to get through a compile without calling BindAssemblySpec on mscorlib. This
577 // is because refs to mscorlib are short circuited in a number of places. So, we will explicitly
578 // add it to our dependencies.
579
580 AssemblySpec mscorlib;
581 mscorlib.InitializeSpec(SystemDomain::SystemFile());
582 GetAppDomain()->BindAssemblySpec(&mscorlib,TRUE,FALSE);
583
584 if (!IsReadyToRunCompilation() && !SystemDomain::SystemFile()->HasNativeImage())
585 {
586 return NGEN_E_SYS_ASM_NI_MISSING;
587 }
588 }
589
590 return S_OK;
591}
592
593IMDInternalImport *
594 CEECompileInfo::GetAssemblyMetaDataImport(CORINFO_ASSEMBLY_HANDLE assembly)
595{
596 STANDARD_VM_CONTRACT;
597
598 IMDInternalImport * import;
599
600 COOPERATIVE_TRANSITION_BEGIN();
601
602 import = ((Assembly*)assembly)->GetManifestImport();
603 import->AddRef();
604
605 COOPERATIVE_TRANSITION_END();
606
607 return import;
608}
609
610IMDInternalImport *
611 CEECompileInfo::GetModuleMetaDataImport(CORINFO_MODULE_HANDLE scope)
612{
613 STANDARD_VM_CONTRACT;
614
615 IMDInternalImport * import;
616
617 COOPERATIVE_TRANSITION_BEGIN();
618
619 import = ((Module*)scope)->GetMDImport();
620 import->AddRef();
621
622 COOPERATIVE_TRANSITION_END();
623
624 return import;
625}
626
627CORINFO_MODULE_HANDLE
628 CEECompileInfo::GetAssemblyModule(CORINFO_ASSEMBLY_HANDLE assembly)
629{
630 STANDARD_VM_CONTRACT;
631
632 CANNOTTHROWCOMPLUSEXCEPTION();
633
634 return (CORINFO_MODULE_HANDLE) ((Assembly*)assembly)->GetManifestModule();
635}
636
637PEDecoder * CEECompileInfo::GetModuleDecoder(CORINFO_MODULE_HANDLE scope)
638{
639 STANDARD_VM_CONTRACT;
640
641 PEDecoder *result;
642
643 COOPERATIVE_TRANSITION_BEGIN();
644
645 //
646 // Note that we go ahead and return the native image if we are using that.
647 // It contains everything we need to ngen. However, the caller must be
648 // aware and check for the native image case, since some fields will need to come
649 // from the CORCOMPILE_ZAP_HEADER rather than the PE headers.
650 //
651
652 PEFile *pFile = ((Module *) scope)->GetFile();
653
654 if (pFile->HasNativeImage())
655 result = pFile->GetLoadedNative();
656 else
657 result = pFile->GetLoadedIL();
658
659 COOPERATIVE_TRANSITION_END();
660
661 return result;
662
663}
664
665void CEECompileInfo::GetModuleFileName(CORINFO_MODULE_HANDLE scope,
666 SString &result)
667{
668 STANDARD_VM_CONTRACT;
669
670 COOPERATIVE_TRANSITION_BEGIN();
671
672 result.Set(((Module*)scope)->GetPath());
673
674 COOPERATIVE_TRANSITION_END();
675}
676
677CORINFO_ASSEMBLY_HANDLE
678 CEECompileInfo::GetModuleAssembly(CORINFO_MODULE_HANDLE module)
679{
680 STANDARD_VM_CONTRACT;
681
682 CANNOTTHROWCOMPLUSEXCEPTION();
683
684 return (CORINFO_ASSEMBLY_HANDLE) GetModule(module)->GetAssembly();
685}
686
687
688#ifdef CROSSGEN_COMPILE
689//
690// Small wrapper to avoid having too many crossgen ifdefs
691//
692class AssemblyForLoadHint
693{
694 IMDInternalImport * m_pMDImport;
695public:
696 AssemblyForLoadHint(IMDInternalImport * pMDImport)
697 : m_pMDImport(pMDImport)
698 {
699 }
700
701 IMDInternalImport * GetManifestImport()
702 {
703 return m_pMDImport;
704 }
705
706 LPCSTR GetSimpleName()
707 {
708 LPCSTR name = "";
709 IfFailThrow(m_pMDImport->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, NULL, &name, NULL, NULL));
710 return name;
711 }
712
713 void GetDisplayName(SString &result, DWORD flags = 0)
714 {
715 PEAssembly::GetFullyQualifiedAssemblyName(m_pMDImport, TokenFromRid(1, mdtAssembly), result, flags);
716 }
717
718 BOOL IsSystem()
719 {
720 return FALSE;
721 }
722};
723#endif
724
725//-----------------------------------------------------------------------------
726// For an assembly with a full name of "Foo, Version=2.0.0.0, Culture=neutral",
727// we want any of these attributes specifications to match:
728// DependencyAttribute("Foo", LoadHint.Always)
729// DependencyAttribute("Foo,", LoadHint.Always)
730// DependencyAttribute("Foo, Version=2.0.0.0, Culture=neutral", LoadHint.Always)
731// The second case of "Foo," is needed only for intra-V2 compat as
732// it was supported at one point during V2. We may be able to get rid of it.
733template <typename ASSEMBLY>
734BOOL IsAssemblySpecifiedInCA(ASSEMBLY * pAssembly, SString dependencyNameFromCA)
735{
736 STANDARD_VM_CONTRACT;
737
738 // First, check for this:
739 // DependencyAttribute("Foo", LoadHint.Always)
740 StackSString simpleName(SString::Utf8, pAssembly->GetSimpleName());
741 if (simpleName.EqualsCaseInsensitive(dependencyNameFromCA, PEImage::GetFileSystemLocale()))
742 return TRUE;
743
744 // Now, check for this:
745 // DependencyAttribute("Foo,", LoadHint.Always)
746 SString comma(W(","));
747 StackSString simpleNameWithComma(simpleName, comma);
748 if (simpleNameWithComma.EqualsCaseInsensitive(dependencyNameFromCA, PEImage::GetFileSystemLocale()))
749 return TRUE;
750
751 // Finally:
752 // DependencyAttribute("Foo, Version=2.0.0.0, Culture=neutral", LoadHint.Always)
753 StackSString fullName;
754 pAssembly->GetDisplayName(fullName);
755 if (fullName.EqualsCaseInsensitive(dependencyNameFromCA))
756 return TRUE;
757
758 return FALSE;
759}
760
761template <typename ASSEMBLY>
762void GetLoadHint(ASSEMBLY * pAssembly, ASSEMBLY *pAssemblyDependency,
763 LoadHintEnum *loadHint, LoadHintEnum *defaultLoadHint = NULL)
764{
765 STANDARD_VM_CONTRACT;
766
767 *loadHint = LoadDefault;
768
769 const BYTE *pbAttr; // Custom attribute data as a BYTE*.
770 ULONG cbAttr; // Size of custom attribute data.
771 mdToken mdAssembly;
772
773 // Look for the binding custom attribute
774 {
775 IMDInternalImport *pImport = pAssembly->GetManifestImport();
776
777 IfFailThrow(pImport->GetAssemblyFromScope(&mdAssembly));
778
779 MDEnumHolder hEnum(pImport); // Enumerator for custom attributes
780 IfFailThrow(pImport->EnumCustomAttributeByNameInit(mdAssembly, DEPENDENCY_TYPE, &hEnum));
781
782 mdCustomAttribute tkAttribute; // A custom attribute on this assembly.
783 while (pImport->EnumNext(&hEnum, &tkAttribute))
784 {
785 // Get raw custom attribute.
786 IfFailThrow(pImport->GetCustomAttributeAsBlob(tkAttribute, (const void**)&pbAttr, &cbAttr));
787
788 CustomAttributeParser cap(pbAttr, cbAttr);
789
790 IfFailThrow(cap.ValidateProlog());
791
792 // Extract string from custom attribute
793 LPCUTF8 szString;
794 ULONG cbString;
795 IfFailThrow(cap.GetNonNullString(&szString, &cbString));
796
797 // Convert the string to Unicode.
798 StackSString dependencyNameFromCA(SString::Utf8, szString, cbString);
799
800 if (IsAssemblySpecifiedInCA(pAssemblyDependency, dependencyNameFromCA))
801 {
802 // Get dependency setting
803 UINT32 u4;
804 IfFailThrow(cap.GetU4(&u4));
805 *loadHint = (LoadHintEnum)u4;
806 break;
807 }
808 }
809 }
810
811 // If not preference is specified, look for the built-in assembly preference
812 if (*loadHint == LoadDefault || defaultLoadHint != NULL)
813 {
814 IMDInternalImport *pImportDependency = pAssemblyDependency->GetManifestImport();
815
816 IfFailThrow(pImportDependency->GetAssemblyFromScope(&mdAssembly));
817
818 HRESULT hr = pImportDependency->GetCustomAttributeByName(mdAssembly,
819 DEFAULTDEPENDENCY_TYPE,
820 (const void**)&pbAttr, &cbAttr);
821 IfFailThrow(hr);
822
823 // Parse the attribute
824 if (hr == S_OK)
825 {
826 CustomAttributeParser cap(pbAttr, cbAttr);
827 IfFailThrow(cap.ValidateProlog());
828
829 // Get default bind setting
830 UINT32 u4 = 0;
831 IfFailThrow(cap.GetU4(&u4));
832
833 if (defaultLoadHint)
834 *defaultLoadHint = (LoadHintEnum) u4;
835 else
836 *loadHint = (LoadHintEnum) u4;
837 }
838 }
839}
840
841HRESULT CEECompileInfo::GetLoadHint(CORINFO_ASSEMBLY_HANDLE hAssembly,
842 CORINFO_ASSEMBLY_HANDLE hAssemblyDependency,
843 LoadHintEnum *loadHint,
844 LoadHintEnum *defaultLoadHint)
845{
846 STANDARD_VM_CONTRACT;
847
848 HRESULT hr = S_OK;
849
850 EX_TRY
851 {
852 Assembly *pAssembly = (Assembly *) hAssembly;
853 Assembly *pAssemblyDependency = (Assembly *) hAssemblyDependency;
854
855 ::GetLoadHint(pAssembly, pAssemblyDependency, loadHint, defaultLoadHint);
856 }
857 EX_CATCH_HRESULT(hr);
858
859 return hr;
860}
861
862HRESULT CEECompileInfo::GetAssemblyVersionInfo(CORINFO_ASSEMBLY_HANDLE hAssembly,
863 CORCOMPILE_VERSION_INFO *pInfo)
864{
865 STANDARD_VM_CONTRACT;
866
867 Assembly *pAssembly = (Assembly *) hAssembly;
868
869 pAssembly->GetDomainAssembly()->GetCurrentVersionInfo(pInfo);
870
871 return S_OK;
872}
873
874void CEECompileInfo::GetAssemblyCodeBase(CORINFO_ASSEMBLY_HANDLE hAssembly, SString &result)
875{
876 STANDARD_VM_CONTRACT;
877
878 COOPERATIVE_TRANSITION_BEGIN();
879
880 Assembly *pAssembly = (Assembly *)hAssembly;
881 _ASSERTE(pAssembly != NULL);
882
883 pAssembly->GetCodeBase(result);
884
885 COOPERATIVE_TRANSITION_END();
886}
887
888//=================================================================================
889
890void FakePromote(PTR_PTR_Object ppObj, ScanContext *pSC, uint32_t dwFlags)
891{
892 CONTRACTL {
893 NOTHROW;
894 GC_NOTRIGGER;
895 MODE_ANY;
896 } CONTRACTL_END;
897
898 CORCOMPILE_GCREFMAP_TOKENS newToken = (dwFlags & GC_CALL_INTERIOR) ? GCREFMAP_INTERIOR : GCREFMAP_REF;
899
900 _ASSERTE((*(CORCOMPILE_GCREFMAP_TOKENS *)ppObj == NULL) || (*(CORCOMPILE_GCREFMAP_TOKENS *)ppObj == newToken));
901
902 *(CORCOMPILE_GCREFMAP_TOKENS *)ppObj = newToken;
903}
904
905//=================================================================================
906
907void FakePromoteCarefully(promote_func *fn, Object **ppObj, ScanContext *pSC, uint32_t dwFlags)
908{
909 (*fn)(ppObj, pSC, dwFlags);
910}
911
912//=================================================================================
913
914void FakeGcScanRoots(MetaSig& msig, ArgIterator& argit, MethodDesc * pMD, BYTE * pFrame)
915{
916 STANDARD_VM_CONTRACT;
917
918 ScanContext sc;
919
920 // Encode generic instantiation arg
921 if (argit.HasParamType())
922 {
923 // Note that intrinsic array methods have hidden instantiation arg too, but it is not reported to GC
924 if (pMD->RequiresInstMethodDescArg())
925 *(CORCOMPILE_GCREFMAP_TOKENS *)(pFrame + argit.GetParamTypeArgOffset()) = GCREFMAP_METHOD_PARAM;
926 else
927 if (pMD->RequiresInstMethodTableArg())
928 *(CORCOMPILE_GCREFMAP_TOKENS *)(pFrame + argit.GetParamTypeArgOffset()) = GCREFMAP_TYPE_PARAM;
929 }
930
931 // If the function has a this pointer, add it to the mask
932 if (argit.HasThis())
933 {
934 BOOL interior = pMD->GetMethodTable()->IsValueType() && !pMD->IsUnboxingStub();
935
936 FakePromote((Object **)(pFrame + argit.GetThisOffset()), &sc, interior ? GC_CALL_INTERIOR : 0);
937 }
938
939 if (argit.IsVarArg())
940 {
941 *(CORCOMPILE_GCREFMAP_TOKENS *)(pFrame + argit.GetVASigCookieOffset()) = GCREFMAP_VASIG_COOKIE;
942
943 // We are done for varargs - the remaining arguments are reported via vasig cookie
944 return;
945 }
946
947 // Also if the method has a return buffer, then it is the first argument, and could be an interior ref,
948 // so always promote it.
949 if (argit.HasRetBuffArg())
950 {
951 FakePromote((Object **)(pFrame + argit.GetRetBuffArgOffset()), &sc, GC_CALL_INTERIOR);
952 }
953
954 //
955 // Now iterate the arguments
956 //
957
958 // Cycle through the arguments, and call msig.GcScanRoots for each
959 int argOffset;
960 while ((argOffset = argit.GetNextOffset()) != TransitionBlock::InvalidOffset)
961 {
962 ArgDestination argDest(pFrame, argOffset, argit.GetArgLocDescForStructInRegs());
963 msig.GcScanRoots(&argDest, &FakePromote, &sc, &FakePromoteCarefully);
964 }
965}
966
967void CEECompileInfo::GetCallRefMap(CORINFO_METHOD_HANDLE hMethod, GCRefMapBuilder * pBuilder, bool isDispatchCell)
968{
969#ifdef _DEBUG
970 DWORD dwInitialLength = pBuilder->GetBlobLength();
971 UINT nTokensWritten = 0;
972#endif
973
974 MethodDesc *pMD = (MethodDesc *)hMethod;
975
976 SigTypeContext typeContext(pMD);
977 PCCOR_SIGNATURE pSig;
978 DWORD cbSigSize;
979 pMD->GetSig(&pSig, &cbSigSize);
980 MetaSig msig(pSig, cbSigSize, pMD->GetModule(), &typeContext);
981
982 //
983 // Shared default interface methods (i.e. virtual interface methods with an implementation) require
984 // an instantiation argument. But if we're in a situation where we haven't resolved the method yet
985 // we need to pretent that unresolved default interface methods are like any other interface
986 // methods and don't have an instantiation argument.
987 // See code:CEEInfo::getMethodSigInternal
988 //
989 assert(!isDispatchCell || !pMD->RequiresInstArg() || pMD->GetMethodTable()->IsInterface());
990 if (pMD->RequiresInstArg() && !isDispatchCell)
991 {
992 msig.SetHasParamTypeArg();
993 }
994
995 ArgIterator argit(&msig);
996
997 UINT nStackBytes = argit.SizeOfFrameArgumentArray();
998
999 // Allocate a fake stack
1000 CQuickBytes qbFakeStack;
1001 qbFakeStack.AllocThrows(sizeof(TransitionBlock) + nStackBytes);
1002 memset(qbFakeStack.Ptr(), 0, qbFakeStack.Size());
1003
1004 BYTE * pFrame = (BYTE *)qbFakeStack.Ptr();
1005
1006 // Fill it in
1007 FakeGcScanRoots(msig, argit, pMD, pFrame);
1008
1009 //
1010 // Encode the ref map
1011 //
1012
1013 UINT nStackSlots;
1014
1015#ifdef _TARGET_X86_
1016 UINT cbStackPop = argit.CbStackPop();
1017 pBuilder->WriteStackPop(cbStackPop / sizeof(TADDR));
1018
1019 nStackSlots = nStackBytes / sizeof(TADDR) + NUM_ARGUMENT_REGISTERS;
1020#else
1021 nStackSlots = (sizeof(TransitionBlock) + nStackBytes - TransitionBlock::GetOffsetOfArgumentRegisters()) / TARGET_POINTER_SIZE;
1022#endif
1023
1024 for (UINT pos = 0; pos < nStackSlots; pos++)
1025 {
1026 int ofs;
1027
1028#ifdef _TARGET_X86_
1029 ofs = (pos < NUM_ARGUMENT_REGISTERS) ?
1030 (TransitionBlock::GetOffsetOfArgumentRegisters() + ARGUMENTREGISTERS_SIZE - (pos + 1) * sizeof(TADDR)) :
1031 (TransitionBlock::GetOffsetOfArgs() + (pos - NUM_ARGUMENT_REGISTERS) * sizeof(TADDR));
1032#else
1033 ofs = TransitionBlock::GetOffsetOfArgumentRegisters() + pos * TARGET_POINTER_SIZE;
1034#endif
1035
1036 CORCOMPILE_GCREFMAP_TOKENS token = *(CORCOMPILE_GCREFMAP_TOKENS *)(pFrame + ofs);
1037
1038 if (token != 0)
1039 {
1040 INDEBUG(nTokensWritten++;)
1041 pBuilder->WriteToken(pos, token);
1042 }
1043 }
1044
1045 // We are done
1046 pBuilder->Flush();
1047
1048#ifdef _DEBUG
1049 //
1050 // Verify that decoder produces what got encoded
1051 //
1052
1053 DWORD dwFinalLength;
1054 PVOID pBlob = pBuilder->GetBlob(&dwFinalLength);
1055
1056 UINT nTokensDecoded = 0;
1057
1058 GCRefMapDecoder decoder((BYTE *)pBlob + dwInitialLength);
1059
1060#ifdef _TARGET_X86_
1061 _ASSERTE(decoder.ReadStackPop() * sizeof(TADDR) == cbStackPop);
1062#endif
1063
1064 while (!decoder.AtEnd())
1065 {
1066 int pos = decoder.CurrentPos();
1067 int token = decoder.ReadToken();
1068
1069 int ofs;
1070
1071#ifdef _TARGET_X86_
1072 ofs = (pos < NUM_ARGUMENT_REGISTERS) ?
1073 (TransitionBlock::GetOffsetOfArgumentRegisters() + ARGUMENTREGISTERS_SIZE - (pos + 1) * sizeof(TADDR)) :
1074 (TransitionBlock::GetOffsetOfArgs() + (pos - NUM_ARGUMENT_REGISTERS) * sizeof(TADDR));
1075#else
1076 ofs = TransitionBlock::GetOffsetOfArgumentRegisters() + pos * TARGET_POINTER_SIZE;
1077#endif
1078
1079 if (token != 0)
1080 {
1081 _ASSERTE(*(CORCOMPILE_GCREFMAP_TOKENS *)(pFrame + ofs) == token);
1082 nTokensDecoded++;
1083 }
1084 }
1085
1086 // Verify that all tokens got decoded.
1087 _ASSERTE(nTokensWritten == nTokensDecoded);
1088#endif // _DEBUG
1089}
1090
1091void CEECompileInfo::CompressDebugInfo(
1092 IN ICorDebugInfo::OffsetMapping * pOffsetMapping,
1093 IN ULONG iOffsetMapping,
1094 IN ICorDebugInfo::NativeVarInfo * pNativeVarInfo,
1095 IN ULONG iNativeVarInfo,
1096 IN OUT SBuffer * pDebugInfoBuffer
1097 )
1098{
1099 STANDARD_VM_CONTRACT;
1100
1101 CompressDebugInfo::CompressBoundariesAndVars(pOffsetMapping, iOffsetMapping, pNativeVarInfo, iNativeVarInfo, pDebugInfoBuffer, NULL);
1102}
1103
1104ICorJitHost* CEECompileInfo::GetJitHost()
1105{
1106 return JitHost::getJitHost();
1107}
1108
1109HRESULT CEECompileInfo::GetBaseJitFlags(
1110 IN CORINFO_METHOD_HANDLE hMethod,
1111 OUT CORJIT_FLAGS *pFlags)
1112{
1113 STANDARD_VM_CONTRACT;
1114
1115 MethodDesc *pMD = (MethodDesc *)hMethod;
1116 *pFlags = CEEInfo::GetBaseCompileFlags(pMD);
1117
1118 return S_OK;
1119}
1120
1121//=================================================================================
1122
1123#ifdef _DEBUG
1124
1125static struct
1126{
1127 size_t total;
1128 size_t noEmbed;
1129 size_t array;
1130 size_t primitives;
1131 size_t szarray;
1132} embedStats;
1133
1134#endif // _DEBUG
1135
1136BOOL CEEPreloader::CanEmbedClassID(CORINFO_CLASS_HANDLE typeHandle)
1137{
1138 STANDARD_VM_CONTRACT;
1139
1140 TypeHandle hnd = (TypeHandle) typeHandle;
1141 return m_image->CanEagerBindToTypeHandle(hnd) &&
1142 !hnd.AsMethodTable()->NeedsCrossModuleGenericsStaticsInfo();
1143}
1144
1145BOOL CEEPreloader::CanEmbedModuleID(CORINFO_MODULE_HANDLE moduleHandle)
1146{
1147 STANDARD_VM_CONTRACT;
1148
1149 return m_image->CanEagerBindToModule((Module *)moduleHandle);
1150}
1151
1152BOOL CEEPreloader::CanEmbedModuleHandle(CORINFO_MODULE_HANDLE moduleHandle)
1153{
1154 STANDARD_VM_CONTRACT;
1155
1156 return m_image->CanEagerBindToModule((Module *)moduleHandle);
1157
1158}
1159BOOL CEEPreloader::CanEmbedClassHandle(CORINFO_CLASS_HANDLE typeHandle)
1160{
1161 STANDARD_VM_CONTRACT;
1162
1163 TypeHandle hnd = (TypeHandle) typeHandle;
1164
1165 BOOL decision = m_image->CanEagerBindToTypeHandle(hnd);
1166
1167#ifdef _DEBUG
1168 embedStats.total++;
1169
1170 if (!decision)
1171 embedStats.noEmbed++;
1172
1173 if (hnd.IsArray())
1174 {
1175 embedStats.array++;
1176
1177 CorElementType arrType = hnd.AsArray()->GetInternalCorElementType();
1178 if (arrType == ELEMENT_TYPE_SZARRAY)
1179 embedStats.szarray++;
1180
1181 CorElementType elemType = hnd.AsArray()->GetArrayElementTypeHandle().GetInternalCorElementType();
1182 if (elemType <= ELEMENT_TYPE_R8)
1183 embedStats.primitives++;
1184 }
1185#endif // _DEBUG
1186 return decision;
1187}
1188
1189
1190/*static*/ BOOL CanEmbedMethodDescViaContext(MethodDesc * pMethod, MethodDesc * pContext)
1191{
1192 STANDARD_VM_CONTRACT;
1193
1194 if (pContext != NULL)
1195 {
1196 _ASSERTE(pContext->GetLoaderModule() == GetAppDomain()->ToCompilationDomain()->GetTargetModule());
1197
1198 // a method can always embed its own handle
1199 if (pContext == pMethod)
1200 {
1201 return TRUE;
1202 }
1203
1204 // Methods that are tightly bound to the same method table can
1205 // always refer each other directly. This check allows methods
1206 // within one speculative generic instantiations to call each
1207 // other directly.
1208 //
1209 if ((pContext->GetMethodTable() == pMethod->GetMethodTable()) &&
1210 pContext->IsTightlyBoundToMethodTable() &&
1211 pMethod->IsTightlyBoundToMethodTable())
1212 {
1213 return TRUE;
1214 }
1215 }
1216 return FALSE;
1217}
1218
1219BOOL CEEPreloader::CanEmbedMethodHandle(CORINFO_METHOD_HANDLE methodHandle,
1220 CORINFO_METHOD_HANDLE contextHandle)
1221{
1222 STANDARD_VM_CONTRACT;
1223
1224 MethodDesc * pContext = GetMethod(contextHandle);
1225 MethodDesc * pMethod = GetMethod(methodHandle);
1226
1227 if (CanEmbedMethodDescViaContext(pMethod, pContext))
1228 return TRUE;
1229
1230 return m_image->CanEagerBindToMethodDesc(pMethod);
1231}
1232
1233BOOL CEEPreloader::CanEmbedFieldHandle(CORINFO_FIELD_HANDLE fieldHandle)
1234{
1235 STANDARD_VM_CONTRACT;
1236
1237 return m_image->CanEagerBindToFieldDesc((FieldDesc *) fieldHandle);
1238
1239}
1240
1241void* CEECompileInfo::GetStubSize(void *pStubAddress, DWORD *pSizeToCopy)
1242{
1243 CONTRACT(void*)
1244 {
1245 STANDARD_VM_CHECK;
1246 PRECONDITION(pStubAddress && pSizeToCopy);
1247 }
1248 CONTRACT_END;
1249
1250 Stub *stub = Stub::RecoverStubAndSize((TADDR)pStubAddress, pSizeToCopy);
1251 _ASSERTE(*pSizeToCopy > sizeof(Stub));
1252 RETURN stub;
1253}
1254
1255HRESULT CEECompileInfo::GetStubClone(void *pStub, BYTE *pBuffer, DWORD dwBufferSize)
1256{
1257 STANDARD_VM_CONTRACT;
1258
1259 if (pStub == NULL)
1260 {
1261 return E_INVALIDARG;
1262 }
1263
1264 return (reinterpret_cast<Stub *>(pStub)->CloneStub(pBuffer, dwBufferSize));
1265}
1266
1267HRESULT CEECompileInfo::GetTypeDef(CORINFO_CLASS_HANDLE classHandle,
1268 mdTypeDef *token)
1269{
1270 STANDARD_VM_CONTRACT;
1271
1272 CANNOTTHROWCOMPLUSEXCEPTION();
1273
1274 TypeHandle hClass(classHandle);
1275
1276 *token = hClass.GetCl();
1277
1278 return S_OK;
1279}
1280
1281HRESULT CEECompileInfo::GetMethodDef(CORINFO_METHOD_HANDLE methodHandle,
1282 mdMethodDef *token)
1283{
1284 STANDARD_VM_CONTRACT;
1285
1286 CANNOTTHROWCOMPLUSEXCEPTION();
1287
1288 *token = ((MethodDesc*)methodHandle)->GetMemberDef();
1289
1290 return S_OK;
1291}
1292
1293/*********************************************************************/
1294// Used to determine if a methodHandle can be embedded in an ngen image.
1295// Depends on what things are persisted by CEEPreloader
1296
1297BOOL CEEPreloader::CanEmbedFunctionEntryPoint(
1298 CORINFO_METHOD_HANDLE methodHandle,
1299 CORINFO_METHOD_HANDLE contextHandle, /* = NULL */
1300 CORINFO_ACCESS_FLAGS accessFlags /*=CORINFO_ACCESS_ANY*/)
1301{
1302 STANDARD_VM_CONTRACT;
1303
1304 MethodDesc * pMethod = GetMethod(methodHandle);
1305
1306 // Methods with native callable attribute are special , since
1307 // they are used as LDFTN targets.Native Callable methods
1308 // uses the same code path as reverse pinvoke and embedding them
1309 // in an ngen image require saving the reverse pinvoke stubs.
1310 if (pMethod->HasNativeCallableAttribute())
1311 return FALSE;
1312
1313 return TRUE;
1314}
1315
1316BOOL CEEPreloader::DoesMethodNeedRestoringBeforePrestubIsRun(
1317 CORINFO_METHOD_HANDLE methodHandle)
1318{
1319 STANDARD_VM_CONTRACT;
1320
1321 MethodDesc * ftn = GetMethod(methodHandle);
1322
1323 // The restore mechanism for InstantiatedMethodDescs (IMDs) is complicated, and causes
1324 // circular dependency complications with the GC if we hardbind to the prestub/precode
1325 // of an unrestored IMD. As such, we're eliminating hardbinding to unrestored MethodDescs
1326 // that belong to generic types.
1327
1328 //@TODO: The reduction may be overkill, and we may consider refining the cases.
1329
1330 // Specifically, InstantiatedMethodDescs can have preferred zap modules different than
1331 // the zap modules for their owning types. As such, in a soft-binding case a MethodDesc
1332 // may not be able to trace back to its owning Module without hitting an unrestored
1333 // fixup token. For example, the 64-bit JIT can not yet provide generic type arguments
1334 // and uses instantiating stubs to call static methods on generic types. If such an stub
1335 // belong to a module other than the module in which the generic type is declared, then
1336 // it is possible for the MethodTable::m_pEEClass or the EEClass::m_pModule pointers to
1337 // be unrestored. The complication arises when a call to the prestub/precode of such
1338 // an unrestored IMD causes us try to restore the IMD and this in turn causes us to
1339 // transition to preemptive GC and as such GC needs the metadata signature from the IMD
1340 // to iterate its arguments. But since we're currently restoring the IMD, we may not be
1341 // able to get to the signature, and as such we're stuck.
1342
1343 // The same problem exists for instantiation arguments. We may need the instantiation
1344 // arguments while walking the signature during GC, and if they are not restored we're stuck.
1345
1346 if (ftn->HasClassOrMethodInstantiation())
1347 {
1348 if (ftn->NeedsRestore(m_image))
1349 return TRUE;
1350 }
1351
1352 return FALSE;
1353}
1354
1355BOOL CEECompileInfo::IsNativeCallableMethod(CORINFO_METHOD_HANDLE handle)
1356{
1357 WRAPPER_NO_CONTRACT;
1358
1359 MethodDesc * pMethod = GetMethod(handle);
1360 return pMethod->HasNativeCallableAttribute();
1361}
1362
1363BOOL CEEPreloader::CanSkipDependencyActivation(CORINFO_METHOD_HANDLE context,
1364 CORINFO_MODULE_HANDLE moduleFrom,
1365 CORINFO_MODULE_HANDLE moduleTo)
1366{
1367 STANDARD_VM_CONTRACT;
1368
1369 // Can't skip any fixups for speculative generic instantiations
1370 if (Module::GetPreferredZapModuleForMethodDesc(GetMethod(context)) != m_image->GetModule())
1371 return FALSE;
1372
1373 // We don't need a fixup for eager bound dependencies since we are going to have
1374 // an uncontional one already.
1375 return m_image->CanEagerBindToModule((Module *)moduleTo);
1376}
1377
1378CORINFO_MODULE_HANDLE CEEPreloader::GetPreferredZapModuleForClassHandle(
1379 CORINFO_CLASS_HANDLE classHnd)
1380{
1381 STANDARD_VM_CONTRACT;
1382
1383 return CORINFO_MODULE_HANDLE(Module::GetPreferredZapModuleForTypeHandle(TypeHandle(classHnd)));
1384}
1385
1386// This method is called directly from zapper
1387extern BOOL CanDeduplicateCode(CORINFO_METHOD_HANDLE method, CORINFO_METHOD_HANDLE duplicateMethod);
1388
1389BOOL CanDeduplicateCode(CORINFO_METHOD_HANDLE method, CORINFO_METHOD_HANDLE duplicateMethod)
1390{
1391 CONTRACTL
1392 {
1393 NOTHROW;
1394 GC_NOTRIGGER;
1395 }
1396 CONTRACTL_END;
1397
1398 ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();
1399
1400 // For now, the deduplication is supported for IL stubs only
1401 DynamicMethodDesc * pMethod = GetMethod(method)->AsDynamicMethodDesc();
1402 DynamicMethodDesc * pDuplicateMethod = GetMethod(duplicateMethod)->AsDynamicMethodDesc();
1403
1404 //
1405 // Make sure that the return types match (for code:Thread::HijackThread)
1406 //
1407
1408#ifdef _TARGET_X86_
1409 MetaSig msig1(pMethod);
1410 MetaSig msig2(pDuplicateMethod);
1411 if (!msig1.HasFPReturn() != !msig2.HasFPReturn())
1412 return FALSE;
1413#endif // _TARGET_X86_
1414
1415 MetaSig::RETURNTYPE returnType = pMethod->ReturnsObject();
1416 MetaSig::RETURNTYPE returnTypeDuplicate = pDuplicateMethod->ReturnsObject();
1417
1418 if (returnType != returnTypeDuplicate)
1419 return FALSE;
1420
1421 //
1422 // Do not enable deduplication of structs returned in registers
1423 //
1424
1425 if (returnType == MetaSig::RETVALUETYPE)
1426 return FALSE;
1427
1428 //
1429 // Make sure that the IL stub flags match
1430 //
1431
1432 if (pMethod->GetExtendedFlags() != pDuplicateMethod->GetExtendedFlags())
1433 return FALSE;
1434
1435 return TRUE;
1436}
1437
1438void CEEPreloader::NoteDeduplicatedCode(CORINFO_METHOD_HANDLE method, CORINFO_METHOD_HANDLE duplicateMethod)
1439{
1440 STANDARD_VM_CONTRACT;
1441
1442#ifndef FEATURE_FULL_NGEN // Deduplication
1443 DuplicateMethodEntry e;
1444 e.pMD = GetMethod(method);
1445 e.pDuplicateMD = GetMethod(duplicateMethod);
1446 m_duplicateMethodsHash.Add(e);
1447#endif
1448}
1449
1450HRESULT CEECompileInfo::GetFieldDef(CORINFO_FIELD_HANDLE fieldHandle,
1451 mdFieldDef *token)
1452{
1453 STANDARD_VM_CONTRACT;
1454
1455 CANNOTTHROWCOMPLUSEXCEPTION();
1456
1457 *token = ((FieldDesc*)fieldHandle)->GetMemberDef();
1458
1459 return S_OK;
1460}
1461
1462void CEECompileInfo::EncodeModuleAsIndex(CORINFO_MODULE_HANDLE fromHandle,
1463 CORINFO_MODULE_HANDLE handle,
1464 DWORD* pIndex,
1465 IMetaDataAssemblyEmit* pAssemblyEmit)
1466{
1467 STANDARD_VM_CONTRACT;
1468
1469 COOPERATIVE_TRANSITION_BEGIN();
1470
1471 Module *fromModule = GetModule(fromHandle);
1472 Assembly *fromAssembly = fromModule->GetAssembly();
1473
1474 Module *module = GetModule(handle);
1475 Assembly *assembly = module->GetAssembly();
1476
1477 if (assembly == fromAssembly)
1478 *pIndex = 0;
1479 else
1480 {
1481 UPTR result;
1482 mdToken token;
1483
1484 CompilationDomain *pDomain = GetAppDomain()->ToCompilationDomain();
1485
1486 RefCache *pRefCache = pDomain->GetRefCache(fromModule);
1487 if (!pRefCache)
1488 ThrowOutOfMemory();
1489
1490
1491 if (!assembly->GetManifestFile()->HasBindableIdentity())
1492 {
1493 // If the module that we'd like to encode for a later fixup doesn't have
1494 // a bindable identity, then this will fail at runtime. So, we ask the
1495 // compilation domain for a matching assembly with a bindable identity.
1496 // This is possible because this module must have been bound in the past,
1497 // and the compilation domain will keep track of at least one corresponding
1498 // bindable identity.
1499 AssemblySpec defSpec;
1500 defSpec.InitializeSpec(assembly->GetManifestFile());
1501
1502 AssemblySpec* pRefSpec = pDomain->FindAssemblyRefSpecForDefSpec(&defSpec);
1503 _ASSERTE(pRefSpec != nullptr);
1504
1505 IfFailThrow(pRefSpec->EmitToken(pAssemblyEmit, &token, TRUE, TRUE));
1506 token += fromModule->GetAssemblyRefMax();
1507 }
1508 else
1509 {
1510 result = pRefCache->m_sAssemblyRefMap.LookupValue((UPTR)assembly, NULL);
1511
1512 if (result == (UPTR)INVALIDENTRY)
1513 token = fromModule->FindAssemblyRef(assembly);
1514 else
1515 token = (mdAssemblyRef) result;
1516
1517 if (IsNilToken(token))
1518 {
1519 token = fromAssembly->AddAssemblyRef(assembly, pAssemblyEmit);
1520 token += fromModule->GetAssemblyRefMax();
1521 }
1522 }
1523
1524 *pIndex = RidFromToken(token);
1525
1526 pRefCache->m_sAssemblyRefMap.InsertValue((UPTR) assembly, (UPTR)token);
1527 }
1528
1529 COOPERATIVE_TRANSITION_END();
1530}
1531
1532void CEECompileInfo::EncodeClass(
1533 CORINFO_MODULE_HANDLE referencingModule,
1534 CORINFO_CLASS_HANDLE classHandle,
1535 SigBuilder * pSigBuilder,
1536 LPVOID pEncodeModuleContext,
1537 ENCODEMODULE_CALLBACK pfnEncodeModule)
1538{
1539 STANDARD_VM_CONTRACT;
1540
1541 TypeHandle th(classHandle);
1542
1543 ZapSig zapSig((Module *)referencingModule, pEncodeModuleContext, ZapSig::NormalTokens,
1544 (EncodeModuleCallback) pfnEncodeModule, NULL);
1545
1546 COOPERATIVE_TRANSITION_BEGIN();
1547
1548 BOOL fSuccess;
1549 fSuccess = zapSig.GetSignatureForTypeHandle(th, pSigBuilder);
1550 _ASSERTE(fSuccess);
1551
1552 COOPERATIVE_TRANSITION_END();
1553}
1554
1555CORINFO_MODULE_HANDLE CEECompileInfo::GetLoaderModuleForMscorlib()
1556{
1557 STANDARD_VM_CONTRACT;
1558
1559 return CORINFO_MODULE_HANDLE(SystemDomain::SystemModule());
1560}
1561
1562CORINFO_MODULE_HANDLE CEECompileInfo::GetLoaderModuleForEmbeddableType(CORINFO_CLASS_HANDLE clsHnd)
1563{
1564 STANDARD_VM_CONTRACT;
1565
1566 TypeHandle t = TypeHandle(clsHnd);
1567 return CORINFO_MODULE_HANDLE(t.GetLoaderModule());
1568}
1569
1570CORINFO_MODULE_HANDLE CEECompileInfo::GetLoaderModuleForEmbeddableMethod(CORINFO_METHOD_HANDLE methHnd)
1571{
1572 STANDARD_VM_CONTRACT;
1573
1574 MethodDesc *pMD = GetMethod(methHnd);
1575 return CORINFO_MODULE_HANDLE(pMD->GetLoaderModule());
1576}
1577
1578CORINFO_MODULE_HANDLE CEECompileInfo::GetLoaderModuleForEmbeddableField(CORINFO_FIELD_HANDLE fieldHnd)
1579{
1580 STANDARD_VM_CONTRACT;
1581
1582 FieldDesc *pFD = (FieldDesc *) fieldHnd;
1583 return CORINFO_MODULE_HANDLE(pFD->GetLoaderModule());
1584}
1585
1586void CEECompileInfo::EncodeMethod(
1587 CORINFO_MODULE_HANDLE referencingModule,
1588 CORINFO_METHOD_HANDLE handle,
1589 SigBuilder * pSigBuilder,
1590 LPVOID pEncodeModuleContext,
1591 ENCODEMODULE_CALLBACK pfnEncodeModule,
1592 CORINFO_RESOLVED_TOKEN * pResolvedToken,
1593 CORINFO_RESOLVED_TOKEN * pConstrainedResolvedToken,
1594 BOOL fEncodeUsingResolvedTokenSpecStreams)
1595{
1596 STANDARD_VM_CONTRACT;
1597
1598 COOPERATIVE_TRANSITION_BEGIN();
1599 MethodDesc *pMethod = GetMethod(handle);
1600
1601 BOOL fSuccess;
1602 fSuccess = ZapSig::EncodeMethod(pMethod,
1603 (Module *) referencingModule,
1604 pSigBuilder,
1605 pEncodeModuleContext,
1606 pfnEncodeModule, NULL,
1607 pResolvedToken, pConstrainedResolvedToken,
1608 fEncodeUsingResolvedTokenSpecStreams);
1609 _ASSERTE(fSuccess);
1610
1611 COOPERATIVE_TRANSITION_END();
1612}
1613
1614mdToken CEECompileInfo::TryEncodeMethodAsToken(
1615 CORINFO_METHOD_HANDLE handle,
1616 CORINFO_RESOLVED_TOKEN * pResolvedToken,
1617 CORINFO_MODULE_HANDLE * referencingModule)
1618{
1619 STANDARD_VM_CONTRACT;
1620
1621 MethodDesc * pMethod = GetMethod(handle);
1622
1623#ifdef FEATURE_READYTORUN_COMPILER
1624 if (IsReadyToRunCompilation())
1625 {
1626 _ASSERTE(pResolvedToken != NULL);
1627
1628 Module * pReferencingModule = (Module *)pResolvedToken->tokenScope;
1629
1630 if (!pReferencingModule->IsInCurrentVersionBubble())
1631 return mdTokenNil;
1632
1633 // If this is a MemberRef with TypeSpec, we might come to here because we resolved the method
1634 // into a non-generic base class in the same version bubble. However, since we don't have the
1635 // proper type context during ExternalMethodFixupWorker, we can't really encode using token
1636 if (pResolvedToken->pTypeSpec != NULL)
1637 return mdTokenNil;
1638
1639 unsigned methodToken = pResolvedToken->token;
1640
1641 switch (TypeFromToken(methodToken))
1642 {
1643 case mdtMethodDef:
1644 if (pReferencingModule->LookupMethodDef(methodToken) != pMethod)
1645 return mdTokenNil;
1646 break;
1647
1648 case mdtMemberRef:
1649 if (pReferencingModule->LookupMemberRefAsMethod(methodToken) != pMethod)
1650 return mdTokenNil;
1651 break;
1652
1653 default:
1654 return mdTokenNil;
1655 }
1656
1657 *referencingModule = CORINFO_MODULE_HANDLE(pReferencingModule);
1658 return methodToken;
1659 }
1660#endif // FEATURE_READYTORUN_COMPILER
1661
1662 Module *pModule = pMethod->GetModule();
1663 if (!pModule->IsInCurrentVersionBubble())
1664 {
1665 Module * pTargetModule = GetAppDomain()->ToCompilationDomain()->GetTargetModule();
1666 *referencingModule = CORINFO_MODULE_HANDLE(pTargetModule);
1667 return pTargetModule->LookupMemberRefByMethodDesc(pMethod);
1668 }
1669 else
1670 {
1671 mdToken defToken = pMethod->GetMemberDef();
1672 if (pModule->LookupMethodDef(defToken) == pMethod)
1673 {
1674 *referencingModule = CORINFO_MODULE_HANDLE(pModule);
1675 return defToken;
1676 }
1677 }
1678
1679 return mdTokenNil;
1680}
1681
1682DWORD CEECompileInfo::TryEncodeMethodSlot(CORINFO_METHOD_HANDLE handle)
1683{
1684 STANDARD_VM_CONTRACT;
1685
1686 MethodDesc * pMethod = GetMethod(handle);
1687
1688#ifdef FEATURE_READYTORUN_COMPILER
1689 if (IsReadyToRunCompilation())
1690 {
1691 // We can only encode real interface methods as slots
1692 if (!pMethod->IsInterface() || pMethod->IsStatic())
1693 return (DWORD)-1;
1694
1695 // And only if the interface lives in the current version bubble
1696 // If may be possible to relax this restriction if we can guarantee that the external interfaces are
1697 // really not changing. We will play it safe for now.
1698 if (!pMethod->GetModule()->IsInCurrentVersionBubble())
1699 return (DWORD)-1;
1700 }
1701#endif
1702
1703 return pMethod->GetSlot();
1704}
1705
1706void EncodeTypeInDictionarySignature(
1707 Module * pInfoModule,
1708 SigPointer ptr,
1709 SigBuilder * pSigBuilder,
1710 LPVOID encodeContext,
1711 ENCODEMODULE_CALLBACK pfnEncodeModule)
1712{
1713 STANDARD_VM_CONTRACT;
1714
1715 CorElementType typ = ELEMENT_TYPE_END;
1716 IfFailThrow(ptr.GetElemType(&typ));
1717
1718 if (typ == ELEMENT_TYPE_INTERNAL)
1719 {
1720 TypeHandle th;
1721
1722 IfFailThrow(ptr.GetPointer((void**)&th));
1723
1724 ZapSig zapSig(pInfoModule, encodeContext, ZapSig::NormalTokens,
1725 (EncodeModuleCallback) pfnEncodeModule, NULL);
1726
1727 //
1728 // Write class
1729 //
1730 BOOL fSuccess;
1731 fSuccess = zapSig.GetSignatureForTypeHandle(th, pSigBuilder);
1732 _ASSERTE(fSuccess);
1733
1734 return;
1735 }
1736 else
1737 if (typ == ELEMENT_TYPE_GENERICINST)
1738 {
1739 //
1740 // SigParser expects ELEMENT_TYPE_MODULE_ZAPSIG to be before ELEMENT_TYPE_GENERICINST
1741 //
1742 SigPointer peek(ptr);
1743 ULONG instType = 0;
1744 IfFailThrow(peek.GetData(&instType));
1745 _ASSERTE(instType == ELEMENT_TYPE_INTERNAL);
1746
1747 TypeHandle th;
1748 IfFailThrow(peek.GetPointer((void **)&th));
1749
1750 Module * pTypeHandleModule = th.GetModule();
1751
1752 if (!pTypeHandleModule->IsInCurrentVersionBubble())
1753 {
1754 pTypeHandleModule = GetAppDomain()->ToCompilationDomain()->GetTargetModule();
1755 }
1756
1757 if (pTypeHandleModule != pInfoModule)
1758 {
1759 DWORD index = pfnEncodeModule(encodeContext, (CORINFO_MODULE_HANDLE)pTypeHandleModule);
1760 _ASSERTE(index != ENCODE_MODULE_FAILED);
1761
1762 pSigBuilder->AppendElementType((CorElementType) ELEMENT_TYPE_MODULE_ZAPSIG);
1763 pSigBuilder->AppendData(index);
1764 }
1765
1766 pSigBuilder->AppendElementType(ELEMENT_TYPE_GENERICINST);
1767
1768 EncodeTypeInDictionarySignature(pTypeHandleModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
1769 IfFailThrow(ptr.SkipExactlyOne());
1770
1771 ULONG argCnt = 0; // Get number of parameters
1772 IfFailThrow(ptr.GetData(&argCnt));
1773 pSigBuilder->AppendData(argCnt);
1774
1775 while (argCnt--)
1776 {
1777 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
1778 IfFailThrow(ptr.SkipExactlyOne());
1779 }
1780
1781 return;
1782 }
1783 else if((CorElementTypeZapSig)typ == ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG)
1784 {
1785 pSigBuilder->AppendElementType((CorElementType)ELEMENT_TYPE_NATIVE_ARRAY_TEMPLATE_ZAPSIG);
1786
1787 IfFailThrow(ptr.GetElemType(&typ));
1788
1789 _ASSERTE(typ == ELEMENT_TYPE_SZARRAY || typ == ELEMENT_TYPE_ARRAY);
1790 }
1791
1792 pSigBuilder->AppendElementType(typ);
1793
1794 if (!CorIsPrimitiveType(typ))
1795 {
1796 switch (typ)
1797 {
1798 case ELEMENT_TYPE_VAR:
1799 case ELEMENT_TYPE_MVAR:
1800 {
1801 ULONG varNum;
1802 // Skip variable number
1803 IfFailThrow(ptr.GetData(&varNum));
1804 pSigBuilder->AppendData(varNum);
1805 }
1806 break;
1807 case ELEMENT_TYPE_OBJECT:
1808 case ELEMENT_TYPE_STRING:
1809 case ELEMENT_TYPE_TYPEDBYREF:
1810 break;
1811
1812 case ELEMENT_TYPE_BYREF: //fallthru
1813 case ELEMENT_TYPE_PTR:
1814 case ELEMENT_TYPE_PINNED:
1815 case ELEMENT_TYPE_SZARRAY:
1816 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
1817 IfFailThrow(ptr.SkipExactlyOne());
1818 break;
1819
1820 case ELEMENT_TYPE_ARRAY:
1821 {
1822 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
1823 IfFailThrow(ptr.SkipExactlyOne());
1824
1825 ULONG rank = 0; // Get rank
1826 IfFailThrow(ptr.GetData(&rank));
1827 pSigBuilder->AppendData(rank);
1828
1829 if (rank)
1830 {
1831 ULONG nsizes = 0;
1832 IfFailThrow(ptr.GetData(&nsizes));
1833 pSigBuilder->AppendData(nsizes);
1834
1835 while (nsizes--)
1836 {
1837 ULONG data = 0;
1838 IfFailThrow(ptr.GetData(&data));
1839 pSigBuilder->AppendData(data);
1840 }
1841
1842 ULONG nlbounds = 0;
1843 IfFailThrow(ptr.GetData(&nlbounds));
1844 pSigBuilder->AppendData(nlbounds);
1845
1846 while (nlbounds--)
1847 {
1848 ULONG data = 0;
1849 IfFailThrow(ptr.GetData(&data));
1850 pSigBuilder->AppendData(data);
1851 }
1852 }
1853 }
1854 break;
1855
1856 default:
1857 _ASSERTE(!"Unexpected element in signature");
1858 }
1859 }
1860}
1861
1862void CEECompileInfo::EncodeGenericSignature(
1863 LPVOID signature,
1864 BOOL fMethod,
1865 SigBuilder * pSigBuilder,
1866 LPVOID encodeContext,
1867 ENCODEMODULE_CALLBACK pfnEncodeModule)
1868{
1869 STANDARD_VM_CONTRACT;
1870
1871 Module * pInfoModule = MscorlibBinder::GetModule();
1872
1873 SigPointer ptr((PCCOR_SIGNATURE)signature);
1874
1875 ULONG entryKind; // DictionaryEntryKind
1876 IfFailThrow(ptr.GetData(&entryKind));
1877 pSigBuilder->AppendData(entryKind);
1878
1879 if (!fMethod)
1880 {
1881 ULONG dictionaryIndex = 0;
1882 IfFailThrow(ptr.GetData(&dictionaryIndex));
1883
1884 pSigBuilder->AppendData(dictionaryIndex);
1885 }
1886
1887 switch (entryKind)
1888 {
1889 case DeclaringTypeHandleSlot:
1890 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
1891 IfFailThrow(ptr.SkipExactlyOne());
1892 // fall through
1893
1894 case TypeHandleSlot:
1895 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
1896 IfFailThrow(ptr.SkipExactlyOne());
1897 break;
1898
1899 case ConstrainedMethodEntrySlot:
1900 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
1901 IfFailThrow(ptr.SkipExactlyOne());
1902 // fall through
1903
1904 case MethodDescSlot:
1905 case MethodEntrySlot:
1906 case DispatchStubAddrSlot:
1907 {
1908 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
1909 IfFailThrow(ptr.SkipExactlyOne());
1910
1911 ULONG methodFlags;
1912 IfFailThrow(ptr.GetData(&methodFlags));
1913 pSigBuilder->AppendData(methodFlags);
1914
1915 if ((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) == 0)
1916 {
1917 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
1918 IfFailThrow(ptr.SkipExactlyOne());
1919 }
1920
1921 ULONG tokenOrSlot;
1922 IfFailThrow(ptr.GetData(&tokenOrSlot));
1923 pSigBuilder->AppendData(tokenOrSlot);
1924
1925 if (methodFlags & ENCODE_METHOD_SIG_MethodInstantiation)
1926 {
1927 DWORD nGenericMethodArgs;
1928 IfFailThrow(ptr.GetData(&nGenericMethodArgs));
1929 pSigBuilder->AppendData(nGenericMethodArgs);
1930
1931 for (DWORD i = 0; i < nGenericMethodArgs; i++)
1932 {
1933 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
1934 IfFailThrow(ptr.SkipExactlyOne());
1935 }
1936 }
1937 }
1938 break;
1939
1940 case FieldDescSlot:
1941 {
1942 EncodeTypeInDictionarySignature(pInfoModule, ptr, pSigBuilder, encodeContext, pfnEncodeModule);
1943 IfFailThrow(ptr.SkipExactlyOne());
1944
1945 DWORD fieldIndex;
1946 IfFailThrow(ptr.GetData(&fieldIndex));
1947 pSigBuilder->AppendData(fieldIndex);
1948 }
1949 break;
1950
1951 default:
1952 _ASSERTE(false);
1953 }
1954
1955 ULONG dictionarySlot;
1956 IfFailThrow(ptr.GetData(&dictionarySlot));
1957 pSigBuilder->AppendData(dictionarySlot);
1958}
1959
1960void CEECompileInfo::EncodeField(
1961 CORINFO_MODULE_HANDLE referencingModule,
1962 CORINFO_FIELD_HANDLE handle,
1963 SigBuilder * pSigBuilder,
1964 LPVOID encodeContext,
1965 ENCODEMODULE_CALLBACK pfnEncodeModule,
1966 CORINFO_RESOLVED_TOKEN * pResolvedToken,
1967 BOOL fEncodeUsingResolvedTokenSpecStreams)
1968{
1969 STANDARD_VM_CONTRACT;
1970
1971 COOPERATIVE_TRANSITION_BEGIN();
1972
1973 ZapSig::EncodeField(GetField(handle),
1974 (Module *) referencingModule,
1975 pSigBuilder,
1976 encodeContext,
1977 pfnEncodeModule,
1978 pResolvedToken,
1979 fEncodeUsingResolvedTokenSpecStreams);
1980
1981 COOPERATIVE_TRANSITION_END();
1982}
1983
1984BOOL CEECompileInfo::IsEmptyString(mdString token,
1985 CORINFO_MODULE_HANDLE module)
1986{
1987 STANDARD_VM_CONTRACT;
1988
1989 BOOL fRet = FALSE;
1990
1991 COOPERATIVE_TRANSITION_BEGIN();
1992
1993 EEStringData strData;
1994 ((Module *)module)->InitializeStringData(token, &strData, NULL);
1995 fRet = (strData.GetCharCount() == 0);
1996
1997 COOPERATIVE_TRANSITION_END();
1998
1999 return fRet;
2000}
2001
2002#ifdef FEATURE_READYTORUN_COMPILER
2003CORCOMPILE_FIXUP_BLOB_KIND CEECompileInfo::GetFieldBaseOffset(
2004 CORINFO_CLASS_HANDLE classHnd,
2005 DWORD * pBaseOffset)
2006{
2007 STANDARD_VM_CONTRACT;
2008
2009 MethodTable * pMT = (MethodTable *)classHnd;
2010 Module * pModule = pMT->GetModule();
2011
2012 if (!pMT->IsLayoutFixedInCurrentVersionBubble())
2013 {
2014 return pMT->IsValueType() ? ENCODE_CHECK_FIELD_OFFSET : ENCODE_FIELD_OFFSET;
2015 }
2016
2017 if (pMT->IsValueType())
2018 {
2019 return ENCODE_NONE;
2020 }
2021
2022 if (pMT->GetParentMethodTable()->IsInheritanceChainLayoutFixedInCurrentVersionBubble())
2023 {
2024 return ENCODE_NONE;
2025 }
2026
2027 if (pMT->HasLayout())
2028 {
2029 // We won't try to be smart for classes with layout.
2030 // They are complex to get right, and very rare anyway.
2031 return ENCODE_FIELD_OFFSET;
2032 }
2033
2034 *pBaseOffset = ReadyToRunInfo::GetFieldBaseOffset(pMT);
2035 return ENCODE_FIELD_BASE_OFFSET;
2036}
2037
2038BOOL CEECompileInfo::NeedsTypeLayoutCheck(CORINFO_CLASS_HANDLE classHnd)
2039{
2040 STANDARD_VM_CONTRACT;
2041
2042 TypeHandle th(classHnd);
2043
2044 if (th.IsTypeDesc())
2045 return FALSE;
2046
2047 MethodTable * pMT = th.AsMethodTable();
2048
2049 if (!pMT->IsValueType())
2050 return FALSE;
2051
2052 // Skip this check for equivalent types. Equivalent types are used for interop that ensures
2053 // matching layout.
2054 if (pMT->GetClass()->IsEquivalentType())
2055 return FALSE;
2056
2057 return !pMT->IsLayoutFixedInCurrentVersionBubble();
2058}
2059
2060extern void ComputeGCRefMap(MethodTable * pMT, BYTE * pGCRefMap, size_t cbGCRefMap);
2061
2062void CEECompileInfo::EncodeTypeLayout(CORINFO_CLASS_HANDLE classHandle, SigBuilder * pSigBuilder)
2063{
2064 STANDARD_VM_CONTRACT;
2065
2066 MethodTable * pMT = TypeHandle(classHandle).AsMethodTable();
2067 _ASSERTE(pMT->IsValueType());
2068
2069 DWORD dwSize = pMT->GetNumInstanceFieldBytes();
2070 DWORD dwAlignment = CEEInfo::getClassAlignmentRequirementStatic(pMT);
2071
2072 DWORD dwFlags = 0;
2073
2074#ifdef FEATURE_HFA
2075 if (pMT->IsHFA())
2076 dwFlags |= READYTORUN_LAYOUT_HFA;
2077#endif
2078
2079 // Check everything
2080 dwFlags |= READYTORUN_LAYOUT_Alignment;
2081 if (dwAlignment == TARGET_POINTER_SIZE)
2082 dwFlags |= READYTORUN_LAYOUT_Alignment_Native;
2083
2084 dwFlags |= READYTORUN_LAYOUT_GCLayout;
2085 if (!pMT->ContainsPointers())
2086 dwFlags |= READYTORUN_LAYOUT_GCLayout_Empty;
2087
2088 pSigBuilder->AppendData(dwFlags);
2089
2090 // Size is checked unconditionally
2091 pSigBuilder->AppendData(dwSize);
2092
2093#ifdef FEATURE_HFA
2094 if (dwFlags & READYTORUN_LAYOUT_HFA)
2095 {
2096 pSigBuilder->AppendData(pMT->GetHFAType());
2097 }
2098#endif
2099
2100 if ((dwFlags & READYTORUN_LAYOUT_Alignment) && !(dwFlags & READYTORUN_LAYOUT_Alignment_Native))
2101 {
2102 pSigBuilder->AppendData(dwAlignment);
2103 }
2104
2105 if ((dwFlags & READYTORUN_LAYOUT_GCLayout) && !(dwFlags & READYTORUN_LAYOUT_GCLayout_Empty))
2106 {
2107 size_t cbGCRefMap = (dwSize / TARGET_POINTER_SIZE + 7) / 8;
2108 _ASSERTE(cbGCRefMap > 0);
2109
2110 BYTE * pGCRefMap = (BYTE *)_alloca(cbGCRefMap);
2111
2112 ComputeGCRefMap(pMT, pGCRefMap, cbGCRefMap);
2113
2114 for (size_t i = 0; i < cbGCRefMap; i++)
2115 pSigBuilder->AppendByte(pGCRefMap[i]);
2116 }
2117}
2118
2119BOOL CEECompileInfo::AreAllClassesFullyLoaded(CORINFO_MODULE_HANDLE moduleHandle)
2120{
2121 STANDARD_VM_CONTRACT;
2122
2123 return ((Module *)moduleHandle)->AreAllClassesFullyLoaded();
2124}
2125
2126int CEECompileInfo::GetVersionResilientTypeHashCode(CORINFO_MODULE_HANDLE moduleHandle, mdToken token)
2127{
2128 STANDARD_VM_CONTRACT;
2129
2130 int dwHashCode;
2131 if (!::GetVersionResilientTypeHashCode(((Module *)moduleHandle)->GetMDImport(), token, &dwHashCode))
2132 ThrowHR(COR_E_BADIMAGEFORMAT);
2133
2134 return dwHashCode;
2135}
2136
2137int CEECompileInfo::GetVersionResilientMethodHashCode(CORINFO_METHOD_HANDLE methodHandle)
2138{
2139 STANDARD_VM_CONTRACT;
2140
2141 return ::GetVersionResilientMethodHashCode(GetMethod(methodHandle));
2142}
2143
2144#endif // FEATURE_READYTORUN_COMPILER
2145
2146BOOL CEECompileInfo::HasCustomAttribute(CORINFO_METHOD_HANDLE method, LPCSTR customAttributeName)
2147{
2148 STANDARD_VM_CONTRACT;
2149
2150 MethodDesc * pMD = GetMethod(method);
2151 return S_OK == pMD->GetMDImport()->GetCustomAttributeByName(pMD->GetMemberDef(), customAttributeName, NULL, NULL);
2152}
2153
2154#define OMFConst_Read 0x0001
2155#define OMFConst_Write 0x0002
2156#define OMFConst_Exec 0x0004
2157#define OMFConst_F32Bit 0x0008
2158#define OMFConst_ReservedBits1 0x00f0
2159#define OMFConst_FSel 0x0100
2160#define OMFConst_FAbs 0x0200
2161#define OMFConst_ReservedBits2 0x0C00
2162#define OMFConst_FGroup 0x1000
2163#define OMFConst_ReservedBits3 0xE000
2164
2165#define OMF_StandardText (OMFConst_FSel|OMFConst_F32Bit|OMFConst_Exec|OMFConst_Read) // 0x10D
2166#define OMF_SentinelType (OMFConst_FAbs|OMFConst_F32Bit) // 0x208
2167
2168
2169// ----------------------------------------------------------------------------
2170// NGEN PDB SUPPORT
2171//
2172// The NGEN PDB format consists of structs stacked together into buffers, which are
2173// passed to the PDB API. For a description of the structures, see
2174// InternalApis\vctools\inc\cvinfo.h.
2175//
2176// The interface to the PDB used below is NGEN-specific, and is exposed via
2177// diasymreader.dll. For a description of this interface, see ISymNGenWriter2 inside
2178// public\devdiv\inc\corsym.h and debugger\sh\symwrtr\ngenpdbwriter.h,cpp
2179// ----------------------------------------------------------------------------
2180
2181#if defined(NO_NGENPDB) && !defined(FEATURE_PERFMAP)
2182BOOL CEECompileInfo::GetIsGeneratingNgenPDB()
2183{
2184 return FALSE;
2185}
2186
2187void CEECompileInfo::SetIsGeneratingNgenPDB(BOOL fGeneratingNgenPDB)
2188{
2189}
2190
2191BOOL IsNgenPDBCompilationProcess()
2192{
2193 return FALSE;
2194}
2195#else
2196BOOL CEECompileInfo::GetIsGeneratingNgenPDB()
2197{
2198 LIMITED_METHOD_DAC_CONTRACT;
2199 return m_fGeneratingNgenPDB;
2200}
2201
2202void CEECompileInfo::SetIsGeneratingNgenPDB(BOOL fGeneratingNgenPDB)
2203{
2204 LIMITED_METHOD_DAC_CONTRACT;
2205 m_fGeneratingNgenPDB = fGeneratingNgenPDB;
2206}
2207
2208BOOL IsNgenPDBCompilationProcess()
2209{
2210 LIMITED_METHOD_DAC_CONTRACT;
2211 return IsCompilationProcess() && g_pCEECompileInfo->GetIsGeneratingNgenPDB();
2212}
2213
2214#endif // NO_NGENPDB && !FEATURE_PERFMAP
2215
2216#ifndef NO_NGENPDB
2217// This is the prototype of "CreateNGenPdbWriter" exported by diasymreader.dll
2218typedef HRESULT (__stdcall *CreateNGenPdbWriter_t)(const WCHAR *pwszNGenImagePath, const WCHAR *pwszPdbPath, void **ppvObj);
2219
2220// Allocator to specify when requesting boundaries information for PDB
2221BYTE* SimpleNew(void *, size_t cBytes)
2222{
2223 CONTRACTL
2224 {
2225 THROWS;
2226 GC_NOTRIGGER;
2227 MODE_ANY;
2228 }
2229 CONTRACTL_END;
2230
2231 BYTE * p = new BYTE[cBytes];
2232 return p;
2233}
2234
2235// PDB convention has any IPs that don't map to source code (e.g., prolog, epilog, etc.)
2236// to be mapped to line number "0xFeeFee".
2237const int kUnmappedIP = 0xFeeFee;
2238
2239
2240// ----------------------------------------------------------------------------
2241// Simple pair of offsets for each source file name. Pair includes its offset into the
2242// PDB string table, and its offset in the files checksum table.
2243//
2244struct DocNameOffsets
2245{
2246 ULONG32 m_dwStrTableOffset;
2247 ULONG32 m_dwChksumTableOffset;
2248 DocNameOffsets(ULONG32 dwStrTableOffset, ULONG32 dwChksumTableOffset)
2249 : m_dwStrTableOffset(dwStrTableOffset), m_dwChksumTableOffset(dwChksumTableOffset)
2250 {
2251 LIMITED_METHOD_CONTRACT;
2252 }
2253
2254 DocNameOffsets()
2255 : m_dwStrTableOffset((ULONG32) -1), m_dwChksumTableOffset((ULONG32) -1)
2256 {
2257 LIMITED_METHOD_CONTRACT;
2258 }
2259};
2260
2261
2262// ----------------------------------------------------------------------------
2263// This is used when creating the hash table which maps source file names to
2264// DocNameOffsets instances. The only interesting stuff here is that:
2265// * Equality is determined by a case-insensitive comparison on the source file
2266// names
2267// * Hashing is done by hashing the source file names
2268//
2269struct DocNameToOffsetMapTraits : public NoRemoveSHashTraits < MapSHashTraits<LPCSTR, DocNameOffsets> >
2270{
2271public:
2272 static BOOL Equals(key_t k1, key_t k2)
2273 {
2274 LIMITED_METHOD_CONTRACT;
2275
2276 if (k1 == NULL && k2 == NULL)
2277 return TRUE;
2278 if (k1 == NULL || k2 == NULL)
2279 return FALSE;
2280 return _stricmp(k1, k2) == 0;
2281 }
2282
2283 static count_t Hash(key_t k)
2284 {
2285 LIMITED_METHOD_CONTRACT;
2286
2287 if (k == NULL)
2288 return 0;
2289 else
2290 return HashiStringA(k);
2291 }
2292
2293 typedef LPCSTR KEY;
2294 typedef DocNameOffsets VALUE;
2295 typedef NoRemoveSHashTraits < MapSHashTraits<LPCSTR, DocNameOffsets> > PARENT;
2296 typedef PARENT::element_t element_t;
2297 static const element_t Null() { LIMITED_METHOD_CONTRACT; return element_t((KEY)0,VALUE((ULONG32) -1, (ULONG32) -1)); }
2298 static bool IsNull(const element_t &e) { LIMITED_METHOD_CONTRACT; return e.Key() == (KEY)0; }
2299};
2300
2301
2302// ----------------------------------------------------------------------------
2303// Hash table that maps the UTF-8 string of a source file name to its corresponding
2304// DocNameToOffsetMapTraits
2305//
2306class DocNameToOffsetMap : public SHash<DocNameToOffsetMapTraits>
2307{
2308 typedef SHash<DocNameToOffsetMapTraits> PARENT;
2309 typedef LPCSTR KEY;
2310 typedef DocNameOffsets VALUE;
2311
2312public:
2313 void Add(KEY key, VALUE value)
2314 {
2315 CONTRACTL
2316 {
2317 THROWS;
2318 GC_NOTRIGGER;
2319 PRECONDITION(key != (KEY)0);
2320 }
2321 CONTRACTL_END;
2322
2323 PARENT::Add(KeyValuePair<KEY,VALUE>(key, value));
2324 }
2325
2326 void AddOrReplace(KEY key, VALUE value)
2327 {
2328 CONTRACTL
2329 {
2330 THROWS;
2331 GC_NOTRIGGER;
2332 PRECONDITION(key != (KEY)0);
2333 }
2334 CONTRACTL_END;
2335
2336 PARENT::AddOrReplace(KeyValuePair<KEY,VALUE>(key, value));
2337 }
2338
2339 BOOL Lookup(KEY key, VALUE* pValue)
2340 {
2341 CONTRACTL
2342 {
2343 NOTHROW;
2344 GC_NOTRIGGER;
2345 PRECONDITION(key != (KEY)0);
2346 }
2347 CONTRACTL_END;
2348
2349 const KeyValuePair<KEY,VALUE> *pRet = PARENT::LookupPtr(key);
2350 if (pRet == NULL)
2351 return FALSE;
2352
2353 *pValue = pRet->Value();
2354 return TRUE;
2355 }
2356};
2357
2358// ----------------------------------------------------------------------------
2359// Simple class to sort ICorDebugInfo::OffsetMapping arrays by IL offset
2360//
2361class QuickSortILNativeMapByIL : public CQuickSort<ICorDebugInfo::OffsetMapping>
2362{
2363 public:
2364 QuickSortILNativeMapByIL(
2365 ICorDebugInfo::OffsetMapping * rgMap,
2366 int cEntries)
2367 : CQuickSort<ICorDebugInfo::OffsetMapping>(rgMap, cEntries)
2368 {
2369 LIMITED_METHOD_CONTRACT;
2370 }
2371
2372 int Compare(ICorDebugInfo::OffsetMapping * pFirst,
2373 ICorDebugInfo::OffsetMapping * pSecond)
2374 {
2375 LIMITED_METHOD_CONTRACT;
2376
2377 if (pFirst->ilOffset < pSecond->ilOffset)
2378 return -1;
2379 else if (pFirst->ilOffset == pSecond->ilOffset)
2380 return 0;
2381 else
2382 return 1;
2383 }
2384};
2385
2386// ----------------------------------------------------------------------------
2387// Simple class to sort IL to Native mapping arrays by Native offset
2388//
2389class QuickSortILNativeMapByNativeOffset : public CQuickSort<ICorDebugInfo::OffsetMapping>
2390{
2391public:
2392 QuickSortILNativeMapByNativeOffset(
2393 ICorDebugInfo::OffsetMapping * rgMap,
2394 int cEntries)
2395 : CQuickSort<ICorDebugInfo::OffsetMapping>(rgMap, cEntries)
2396 {
2397 LIMITED_METHOD_CONTRACT;
2398 }
2399
2400 int Compare(ICorDebugInfo::OffsetMapping * pFirst,
2401 ICorDebugInfo::OffsetMapping * pSecond)
2402 {
2403 LIMITED_METHOD_CONTRACT;
2404
2405 if (pFirst->nativeOffset < pSecond->nativeOffset)
2406 return -1;
2407 else if (pFirst->nativeOffset == pSecond->nativeOffset)
2408 return 0;
2409 else
2410 return 1;
2411 }
2412};
2413
2414// ----------------------------------------------------------------------------
2415// Simple structure used when merging the JIT manager's IL-to-native maps
2416// (ICorDebugInfo::OffsetMapping) with the IL PDB's source-to-IL map.
2417//
2418struct MapIndexPair
2419{
2420public:
2421 // Index into ICorDebugInfo::OffsetMapping
2422 ULONG32 m_iIlNativeMap;
2423
2424 // Corresponding index into the IL PDB's sequence point arrays
2425 ULONG32 m_iSeqPoints;
2426
2427 MapIndexPair() :
2428 m_iIlNativeMap((ULONG32) -1),
2429 m_iSeqPoints((ULONG32) -1)
2430 {
2431 LIMITED_METHOD_CONTRACT;
2432 }
2433};
2434
2435// ----------------------------------------------------------------------------
2436// Simple class to sort MapIndexPairs by native IP offset. A MapIndexPair sorts "earlier"
2437// if its m_iIlNativeMap index gives you an IP offset (i.e.,
2438// m_rgIlNativeMap[m_iIlNativeMap].nativeOffset) that is smaller.
2439//
2440class QuickSortMapIndexPairsByNativeOffset : public CQuickSort<MapIndexPair>
2441{
2442 public:
2443 QuickSortMapIndexPairsByNativeOffset(
2444 MapIndexPair * rgMap,
2445 int cEntries,
2446 ICorDebugInfo::OffsetMapping * rgIlNativeMap,
2447 ULONG32 cIlNativeMap)
2448 : CQuickSort<MapIndexPair>(rgMap, cEntries),
2449 m_rgIlNativeMap(rgIlNativeMap),
2450 m_cIlNativeMap(cIlNativeMap)
2451 {
2452 LIMITED_METHOD_CONTRACT;
2453 }
2454
2455 int Compare(MapIndexPair * pFirst,
2456 MapIndexPair * pSecond)
2457 {
2458 LIMITED_METHOD_CONTRACT;
2459
2460 _ASSERTE(pFirst->m_iIlNativeMap < m_cIlNativeMap);
2461 _ASSERTE(pSecond->m_iIlNativeMap < m_cIlNativeMap);
2462
2463 DWORD dwFirstNativeOffset = m_rgIlNativeMap[pFirst->m_iIlNativeMap].nativeOffset;
2464 DWORD dwSecondNativeOffset = m_rgIlNativeMap[pSecond->m_iIlNativeMap].nativeOffset;
2465
2466 if (dwFirstNativeOffset < dwSecondNativeOffset)
2467 return -1;
2468 else if (dwFirstNativeOffset == dwSecondNativeOffset)
2469 return 0;
2470 else
2471 return 1;
2472 }
2473
2474protected:
2475 ICorDebugInfo::OffsetMapping * m_rgIlNativeMap;
2476 ULONG32 m_cIlNativeMap;
2477};
2478
2479// ----------------------------------------------------------------------------
2480// The following 3 classes contain the code to generate PDBs
2481//
2482
2483// NGEN always generates PDBs with public symbols lists (so tools can map IP ranges to
2484// methods). This bitmask indicates what extra info should be added to the PDB
2485enum PDBExtraData
2486{
2487 // Add string table subsection, files checksum subsection, and lines subsection to
2488 // allow tools to map IP ranges to source lines.
2489 kPDBLines = 0x00000001,
2490};
2491
2492
2493// ----------------------------------------------------------------------------
2494// Manages generating all PDB data for an NGENd image. One of these is instantiated per
2495// run of "ngen createpdb"
2496//
2497class NGenPdbWriter
2498{
2499private:
2500 CreateNGenPdbWriter_t m_Create;
2501 HMODULE m_hModule;
2502 ReleaseHolder<ISymUnmanagedBinder> m_pBinder;
2503 LPCWSTR m_wszPdbPath;
2504 DWORD m_dwExtraData;
2505 LPCWSTR m_wszManagedPDBSearchPath;
2506
2507public:
2508 NGenPdbWriter (LPCWSTR wszNativeImagePath, LPCWSTR wszPdbPath, DWORD dwExtraData, LPCWSTR wszManagedPDBSearchPath)
2509 : m_Create(NULL),
2510 m_hModule(NULL),
2511 m_wszPdbPath(wszPdbPath),
2512 m_dwExtraData(dwExtraData),
2513 m_wszManagedPDBSearchPath(wszManagedPDBSearchPath)
2514 {
2515 LIMITED_METHOD_CONTRACT;
2516 }
2517
2518#define WRITER_LOAD_ERROR_MESSAGE W("Unable to load ") NATIVE_SYMBOL_READER_DLL W(". Please ensure that ") NATIVE_SYMBOL_READER_DLL W(" is on the path. Error='%d'\n")
2519
2520 HRESULT Load(LPCWSTR wszDiasymreaderPath = nullptr)
2521 {
2522 STANDARD_VM_CONTRACT;
2523
2524 HRESULT hr = S_OK;
2525
2526 m_hModule = WszLoadLibrary(wszDiasymreaderPath != nullptr ? wszDiasymreaderPath : (LPCWSTR)NATIVE_SYMBOL_READER_DLL);
2527 if (m_hModule == NULL)
2528 {
2529 hr = HRESULT_FROM_WIN32(GetLastError());
2530 GetSvcLogger()->Printf(WRITER_LOAD_ERROR_MESSAGE, GetLastError());
2531 return hr;
2532 }
2533
2534 m_Create = reinterpret_cast<CreateNGenPdbWriter_t>(GetProcAddress(m_hModule, "CreateNGenPdbWriter"));
2535 if (m_Create == NULL)
2536 {
2537 hr = HRESULT_FROM_WIN32(GetLastError());
2538 GetSvcLogger()->Printf(WRITER_LOAD_ERROR_MESSAGE, GetLastError());
2539 return hr;
2540 }
2541
2542 if ((m_dwExtraData & kPDBLines) != 0)
2543 {
2544 hr = FakeCoCreateInstanceEx(
2545 CLSID_CorSymBinder_SxS,
2546 wszDiasymreaderPath != nullptr ? wszDiasymreaderPath : (LPCWSTR)NATIVE_SYMBOL_READER_DLL,
2547 IID_ISymUnmanagedBinder,
2548 (void**)&m_pBinder,
2549 NULL);
2550 }
2551
2552 return hr;
2553 }
2554
2555 HRESULT WritePDBDataForModule(Module * pModule);
2556
2557 ~NGenPdbWriter()
2558 {
2559 LIMITED_METHOD_CONTRACT;
2560
2561 if (m_hModule)
2562 FreeLibrary(m_hModule);
2563
2564 m_Create = NULL;
2565 }
2566};
2567
2568#define UNKNOWN_SOURCE_FILE_PATH W("unknown")
2569
2570// ----------------------------------------------------------------------------
2571// Manages generating all PDB data for an EE Module. Directly responsible for writing the
2572// string table and file checksum subsections. One of these is instantiated per Module
2573// found when using the ModuleIterator over the CORINFO_ASSEMBLY_HANDLE corresponding to
2574// this invocation of NGEN createpdb.
2575//
2576class NGenModulePdbWriter
2577{
2578private:
2579 // Simple holder to coordinate the PDB calls to OpenModW and CloseMod on a given PDB
2580 // Mod *.
2581 class PDBModHolder
2582 {
2583 private:
2584 ReleaseHolder<ISymNGenWriter2> m_pWriter;
2585 LPBYTE m_pMod;
2586
2587 public:
2588 PDBModHolder()
2589 : m_pWriter(NULL),
2590 m_pMod(NULL)
2591 {
2592 LIMITED_METHOD_CONTRACT;
2593 }
2594
2595 ~PDBModHolder()
2596 {
2597 LIMITED_METHOD_CONTRACT;
2598
2599 if ((m_pWriter != NULL) && (m_pMod != NULL))
2600 {
2601 m_pWriter->CloseMod(m_pMod);
2602 }
2603 }
2604
2605 HRESULT Open(ISymNGenWriter2 * pWriter, LPCWSTR wszModule, LPCWSTR wszObjFile)
2606 {
2607 LIMITED_METHOD_CONTRACT;
2608
2609 _ASSERTE(m_pWriter == NULL);
2610
2611 m_pWriter = pWriter;
2612 m_pWriter->AddRef();
2613
2614 _ASSERTE(m_pMod == NULL);
2615
2616 HRESULT hr = m_pWriter->OpenModW(wszModule, wszObjFile, &m_pMod);
2617 if (FAILED(hr))
2618 {
2619 m_pMod = NULL;
2620 }
2621 return hr;
2622 }
2623
2624 LPBYTE GetModPtr()
2625 {
2626 LIMITED_METHOD_CONTRACT;
2627
2628 _ASSERTE(m_pMod != NULL);
2629 return m_pMod;
2630 }
2631 };
2632
2633private:
2634 // This holder ensures we delete a half-generated PDB file if we manage to create it
2635 // on disk, but fail at some point after it was created. When NGenModulePdbWriter is
2636 // destroyed, m_deletePDBFileHolder's destructor will delete the PDB file if there
2637 // was a prior error.
2638 //
2639 //************* NOTE! *************
2640 //
2641 // These members should appear FIRST so that they get destructed last. That way, if
2642 // we encounter an error generating the PDB file, we ensure that we release all PDB
2643 // interfaces and close the PDB file BEFORE this holder tries to *delete* the PDB
2644 // file. Also, keep these two in this relative order, so that m_deletePDBFileHolder
2645 // is destructed before m_wszPDBFilePath.
2646 WCHAR m_wszPDBFilePath[MAX_LONGPATH];
2647 DeleteFileHolder m_deletePDBFileHolder;
2648 //
2649 // ************* NOTE! *************
2650
2651 CreateNGenPdbWriter_t m_Create;
2652 LPCWSTR m_wszPdbPath;
2653 ReleaseHolder<ISymNGenWriter2> m_pWriter;
2654 Module * m_pModule;
2655 DWORD m_dwExtraData;
2656 LPCWSTR m_wszManagedPDBSearchPath;
2657
2658 // Currently The DiasymWriter does not use the correct PDB signature for NGEN PDBS unless
2659 // the NGEN DLL whose symbols are being generated end in .ni.dll. Thus we copy
2660 // to this name if it does not follow this covention (as is true with readyToRun
2661 // dlls). This variable remembers this temp file path so we can delete it after
2662 // Pdb generation. If DiaSymWriter is fixed, we can remove this.
2663 SString m_tempSourceDllName;
2664
2665 // Interfaces for reading IL PDB info
2666 ReleaseHolder<ISymUnmanagedBinder> m_pBinder;
2667 ReleaseHolder<ISymUnmanagedReader> m_pReader;
2668 NewInterfaceArrayHolder<ISymUnmanagedDocument> m_rgpDocs; // All docs in the PDB Mod
2669 // I know m_ilPdbCount and m_finalPdbDocCount are confusing.Here is the reason :
2670 // For NGenMethodLinesPdbWriter::WriteDebugSILLinesSubsection, we won't write the path info.
2671 // In order to let WriteDebugSILLinesSubsection find "UNKNOWN_SOURCE_FILE_PATH" which does
2672 // not exist in m_rgpDocs, no matter if we have IL PDB or not, we let m_finalPdbDocCount
2673 // equal m_ilPdbDocCount + 1 and write the extra one path as "UNKNOWN_SOURCE_FILE_PATH"
2674 ULONG32 m_ilPdbDocCount;
2675 ULONG32 m_finalPdbDocCount;
2676
2677 // Keeps track of source file names and how they map to offsets in the relevant PDB
2678 // subsections.
2679 DocNameToOffsetMap m_docNameToOffsetMap;
2680
2681 // Holds a PDB Mod *
2682 PDBModHolder m_pdbMod;
2683
2684 // Buffer in which to store the entire string table (i.e., list of all source file
2685 // names). This buffer is held alive as long as m_docNameToOffsetMap is needed, as
2686 // the latter contains offsets into this buffer.
2687 NewArrayHolder<BYTE> m_rgbStringTableSubsection;
2688
2689 HRESULT InitILPdbData();
2690 HRESULT WriteStringTable();
2691 HRESULT WriteFileChecksums();
2692
2693public:
2694 NGenModulePdbWriter(CreateNGenPdbWriter_t Create, LPCWSTR wszPdbPath, DWORD dwExtraData, ISymUnmanagedBinder * pBinder, Module * pModule, LPCWSTR wszManagedPDBSearchPath)
2695 : m_Create(Create),
2696 m_wszPdbPath(wszPdbPath),
2697 m_pWriter(NULL),
2698 m_pModule(pModule),
2699 m_dwExtraData(dwExtraData),
2700 m_wszManagedPDBSearchPath(wszManagedPDBSearchPath),
2701 m_pBinder(pBinder),
2702 m_ilPdbDocCount(0),
2703 m_finalPdbDocCount(1)
2704 {
2705 LIMITED_METHOD_CONTRACT;
2706
2707 if (m_pBinder != NULL)
2708 m_pBinder->AddRef();
2709
2710 ZeroMemory(m_wszPDBFilePath, sizeof(m_wszPDBFilePath));
2711 }
2712
2713 ~NGenModulePdbWriter();
2714
2715 HRESULT WritePDBData();
2716
2717 HRESULT WriteMethodPDBData(PEImageLayout * pLoadedLayout, USHORT iCodeSection, BYTE *pCodeBase, MethodDesc * hotDesc, PCODE start, bool isILPDBProvided);
2718};
2719
2720// ----------------------------------------------------------------------------
2721// Manages generating the lines subsection in the PDB data for a given managed method.
2722// One of these is instantiated per managed method we find when iterating through all
2723// methods in a Module.
2724//
2725class NGenMethodLinesPdbWriter
2726{
2727private:
2728 ISymNGenWriter2 * m_pWriter;
2729 LPBYTE m_pMod;
2730 ISymUnmanagedReader * m_pReader;
2731 MethodDesc * m_hotDesc;
2732 PCODE m_start;
2733 USHORT m_iCodeSection;
2734 TADDR m_addrCodeSection;
2735 const IJitManager::MethodRegionInfo * m_pMethodRegionInfo;
2736 EECodeInfo * m_pCodeInfo;
2737 DocNameToOffsetMap * m_pDocNameToOffsetMap;
2738 bool m_isILPDBProvided;
2739
2740 // IL-to-native map from JIT manager
2741 ULONG32 m_cIlNativeMap;
2742 NewArrayHolder<ICorDebugInfo::OffsetMapping> m_rgIlNativeMap;
2743
2744 // IL PDB info for this one method
2745 NewInterfaceArrayHolder<ISymUnmanagedDocument> m_rgpDocs; // Source files defining this method.
2746 NewArrayHolder<ULONG32> m_rgilOffsets; // Array of IL offsets for this method
2747 NewArrayHolder<ULONG32> m_rgnLineStarts; // Array of source lines for this method
2748 ULONG32 m_cSeqPoints; // Count of above two parallel arrays
2749
2750 HRESULT WriteNativeILMapPDBData();
2751 LPBYTE InitDebugLinesHeaderSection(
2752 DEBUG_S_SUBSECTION_TYPE type,
2753 ULONG32 ulCodeStartOffset,
2754 ULONG32 cbCode,
2755 ULONG32 lineSize,
2756 CV_DebugSSubsectionHeader_t **ppSubSectHeader /*out*/,
2757 CV_DebugSLinesHeader_t ** ppLinesHeader /*out*/,
2758 LPBYTE * ppbLinesSubsectionCur /*out*/);
2759
2760 HRESULT WriteDebugSLinesSubsection(
2761 ULONG32 ulCodeStartOffset,
2762 ULONG32 cbCode,
2763 MapIndexPair * rgMapIndexPairs,
2764 ULONG32 cMapIndexPairs);
2765
2766 HRESULT WriteDebugSILLinesSubsection(
2767 ULONG32 ulCodeStartOffset,
2768 ULONG32 cbCode,
2769 ICorDebugInfo::OffsetMapping * rgILNativeMap,
2770 ULONG32 rgILNativeMapAdjustSize);
2771
2772 BOOL FinalizeLinesFileBlock(
2773 CV_DebugSLinesFileBlockHeader_t * pLinesFileBlockHeader,
2774 CV_Line_t * pLineBlockStart,
2775 CV_Line_t * pLineBlockAfterEnd
2776#ifdef _DEBUG
2777 , BOOL ignorekUnmappedIPCheck = false
2778#endif
2779 );
2780
2781public:
2782 NGenMethodLinesPdbWriter(
2783 ISymNGenWriter2 * pWriter,
2784 LPBYTE pMod,
2785 ISymUnmanagedReader * pReader,
2786 MethodDesc * hotDesc,
2787 PCODE start,
2788 USHORT iCodeSection,
2789 TADDR addrCodeSection,
2790 const IJitManager::MethodRegionInfo * pMethodRegionInfo,
2791 EECodeInfo * pCodeInfo,
2792 DocNameToOffsetMap * pDocNameToOffsetMap,
2793 bool isILPDBProvided)
2794 : m_pWriter(pWriter),
2795 m_pMod(pMod),
2796 m_pReader(pReader),
2797 m_hotDesc(hotDesc),
2798 m_start(start),
2799 m_iCodeSection(iCodeSection),
2800 m_addrCodeSection(addrCodeSection),
2801 m_pMethodRegionInfo(pMethodRegionInfo),
2802 m_pCodeInfo(pCodeInfo),
2803 m_pDocNameToOffsetMap(pDocNameToOffsetMap),
2804 m_isILPDBProvided(isILPDBProvided),
2805 m_cIlNativeMap(0),
2806 m_cSeqPoints(0)
2807 {
2808 LIMITED_METHOD_CONTRACT;
2809 }
2810
2811 HRESULT WritePDBData();
2812};
2813
2814// ----------------------------------------------------------------------------
2815// NGenPdbWriter implementation
2816
2817
2818
2819//---------------------------------------------------------------------------------------
2820//
2821// Coordinates calling all the other classes & methods to generate PDB info for the
2822// given Module
2823//
2824// Arguments:
2825// pModule - EE Module to write PDB data for
2826//
2827
2828HRESULT NGenPdbWriter::WritePDBDataForModule(Module * pModule)
2829{
2830 STANDARD_VM_CONTRACT;
2831 NGenModulePdbWriter ngenModulePdbWriter(m_Create, m_wszPdbPath, m_dwExtraData, m_pBinder, pModule, m_wszManagedPDBSearchPath);
2832 return ngenModulePdbWriter.WritePDBData();
2833}
2834
2835
2836// ----------------------------------------------------------------------------
2837// NGenModulePdbWriter implementation
2838
2839
2840//---------------------------------------------------------------------------------------
2841//
2842// Writes out all source files into the string table subsection for the PDB Mod*
2843// controlled by this NGenModulePdbWriter. Updates m_docNameToOffsetMap to add string
2844// table offset for each source file as it gets added.
2845//
2846HRESULT NGenModulePdbWriter::WriteStringTable()
2847{
2848 STANDARD_VM_CONTRACT;
2849
2850 _ASSERTE(m_pWriter != NULL);
2851
2852 HRESULT hr;
2853 UINT64 cbStringTableEstimate =
2854 sizeof(DWORD) +
2855 sizeof(CV_DebugSSubsectionHeader_t) +
2856 m_finalPdbDocCount * (MAX_LONGPATH + 1);
2857 if (!FitsIn<ULONG32>(cbStringTableEstimate))
2858 {
2859 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
2860 }
2861
2862 m_rgbStringTableSubsection = new BYTE[ULONG32(cbStringTableEstimate)];
2863 LPBYTE pbStringTableSubsectionCur = m_rgbStringTableSubsection;
2864
2865 // Subsection signature
2866 *((DWORD *) pbStringTableSubsectionCur) = CV_SIGNATURE_C13;
2867 pbStringTableSubsectionCur += sizeof(DWORD);
2868
2869 // Subsection header
2870 CV_DebugSSubsectionHeader_t * pSubSectHeader = (CV_DebugSSubsectionHeader_t *) pbStringTableSubsectionCur;
2871 memset(pSubSectHeader, 0, sizeof(*pSubSectHeader));
2872 pSubSectHeader->type = DEBUG_S_STRINGTABLE;
2873 pbStringTableSubsectionCur += sizeof(*pSubSectHeader);
2874 // pSubSectHeader->cbLen counts the number of bytes that appear AFTER the subsection
2875 // header above (i.e., the size of the string table itself). We'll fill out
2876 // pSubSectHeader->cbLen below, once it's calculated
2877
2878 LPBYTE pbStringTableStart = pbStringTableSubsectionCur;
2879
2880 // The actual strings
2881 for (ULONG32 i = 0; i < m_finalPdbDocCount; i++)
2882 {
2883 // For NGenMethodLinesPdbWriter::WriteDebugSILLinesSubsection, we won't write the path info.
2884 // In order to let WriteDebugSILLinesSubsection can find "UNKNOWN_SOURCE_FILE_PATH" which is
2885 // not existed in m_rgpDocs, no matter we have IL PDB or not, we let m_finalPdbDocCount equals to
2886 // m_ilPdbDocCount + 1 and write the extra one path as "UNKNOWN_SOURCE_FILE_PATH". That also explains
2887 // why we have a inconsistence between m_finalPdbDocCount and m_ilPdbDocCount.
2888 WCHAR wszURL[MAX_LONGPATH] = UNKNOWN_SOURCE_FILE_PATH;
2889 ULONG32 cchURL;
2890 if (i < m_ilPdbDocCount)
2891 {
2892 hr = m_rgpDocs[i]->GetURL(_countof(wszURL), &cchURL, wszURL);
2893 if (FAILED(hr))
2894 return hr;
2895 }
2896 int cbWritten = WideCharToMultiByte(
2897 CP_UTF8,
2898 0, // dwFlags
2899 wszURL,
2900 -1, // i.e., input is NULL-terminated
2901 (LPSTR) pbStringTableSubsectionCur, // output: UTF8 string starts here
2902 ULONG32(cbStringTableEstimate) -
2903 int(pbStringTableSubsectionCur - m_rgbStringTableSubsection), // Available space
2904 NULL, // lpDefaultChar
2905 NULL // lpUsedDefaultChar
2906 );
2907 if (cbWritten == 0)
2908 return HRESULT_FROM_WIN32(GetLastError());
2909
2910 // Remember the string table offset for later
2911 m_docNameToOffsetMap.AddOrReplace(
2912 (LPCSTR) pbStringTableSubsectionCur,
2913 DocNameOffsets(
2914 ULONG32(pbStringTableSubsectionCur - pbStringTableStart),
2915 (ULONG32) -1));
2916
2917 pbStringTableSubsectionCur += cbWritten;
2918 if (pbStringTableSubsectionCur >= (m_rgbStringTableSubsection + cbStringTableEstimate))
2919 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
2920 }
2921
2922 // Now that we know pSubSectHeader->cbLen, fill it in
2923 pSubSectHeader->cbLen = CV_off32_t(pbStringTableSubsectionCur - pbStringTableStart);
2924
2925 // Subsection is now filled out, so use the PDB API to add it
2926 hr = m_pWriter->ModAddSymbols(
2927 m_pdbMod.GetModPtr(),
2928 m_rgbStringTableSubsection,
2929 int(pbStringTableSubsectionCur - m_rgbStringTableSubsection));
2930 if (FAILED(hr))
2931 return hr;
2932
2933 return S_OK;
2934}
2935
2936//---------------------------------------------------------------------------------------
2937//
2938// This takes care of actually loading the IL PDB itself, and initializing the
2939// ISymUnmanaged* interfaces with module-level data from the IL PDB.
2940//
2941HRESULT NGenModulePdbWriter::InitILPdbData()
2942{
2943 // Load the managed PDB
2944
2945 ReleaseHolder<IUnknown> pUnk = NULL;
2946 HRESULT hr = m_pModule->GetReadablePublicMetaDataInterface(ofReadOnly, IID_IMetaDataImport, (LPVOID *) &pUnk);
2947 if (FAILED(hr))
2948 {
2949 GetSvcLogger()->Printf(
2950 W("Unable to obtain metadata for '%s' Error: '0x%x'.\n"),
2951 LPCWSTR(m_pModule->GetFile()->GetILimage()->GetPath()),
2952 hr);
2953 return hr;
2954 }
2955
2956 hr = m_pBinder->GetReaderForFile(
2957 pUnk,
2958 m_pModule->GetFile()->GetILimage()->GetPath(),
2959 m_wszManagedPDBSearchPath,
2960 &m_pReader);
2961 if (FAILED(hr))
2962 {
2963 GetSvcLogger()->Printf(
2964 W("Unable to find managed PDB matching '%s'. Managed PDB search path: '%s'\n"),
2965 LPCWSTR(m_pModule->GetFile()->GetILimage()->GetPath()),
2966 (((m_wszManagedPDBSearchPath == NULL) || (*m_wszManagedPDBSearchPath == W('\0'))) ?
2967 W("(not specified)") :
2968 m_wszManagedPDBSearchPath));
2969 return hr;
2970 }
2971
2972 GetSvcLogger()->Log(W("Loaded managed PDB"));
2973
2974 // Grab the full path of the managed PDB so we can log it
2975 WCHAR wszIlPdbPath[MAX_LONGPATH];
2976 ULONG32 cchIlPdbPath;
2977 hr = m_pReader->GetSymbolStoreFileName(
2978 _countof(wszIlPdbPath),
2979 &cchIlPdbPath,
2980 wszIlPdbPath);
2981 if (FAILED(hr))
2982 {
2983 GetSvcLogger()->Log(W("\n"));
2984 }
2985 else
2986 {
2987 GetSvcLogger()->Printf(W(": '%s'\n"), wszIlPdbPath);
2988 }
2989
2990 // Read all source files names from the IL PDB
2991 ULONG32 cDocs;
2992 hr = m_pReader->GetDocuments(
2993 0, // cDocsRequested
2994 &cDocs,
2995 NULL // Array
2996 );
2997 if (FAILED(hr))
2998 return hr;
2999
3000 m_rgpDocs = new ISymUnmanagedDocument * [cDocs];
3001 hr = m_pReader->GetDocuments(
3002 cDocs,
3003 &m_ilPdbDocCount,
3004 m_rgpDocs);
3005 if (FAILED(hr))
3006 return hr;
3007 m_finalPdbDocCount = m_ilPdbDocCount + 1;
3008 // Commit m_rgpDocs to calling Release() on each ISymUnmanagedDocument* in the array
3009 m_rgpDocs.SetElementCount(m_ilPdbDocCount);
3010
3011 return S_OK;
3012}
3013
3014NGenModulePdbWriter::~NGenModulePdbWriter()
3015{
3016 // Delete any temporary files we created.
3017 if (m_tempSourceDllName.GetCount() != 0)
3018 DeleteFileW(m_tempSourceDllName);
3019 m_tempSourceDllName.Clear();
3020}
3021
3022//---------------------------------------------------------------------------------------
3023//
3024// This manages writing all Module-level data to the PDB, including public symbols,
3025// string table, files checksum, section contribution table, and, indirectly, the lines
3026// subsection
3027//
3028HRESULT NGenModulePdbWriter::WritePDBData()
3029{
3030 STANDARD_VM_CONTRACT;
3031
3032 _ASSERTE(m_pWriter == NULL);
3033
3034 HRESULT hr;
3035
3036 // This will try to open the managed PDB if lines info was requested. This is a
3037 // likely failure point, so intentionally do this before creating the NGEN PDB file
3038 // on disk.
3039 bool isILPDBProvided = false;
3040 if ((m_dwExtraData & kPDBLines) != 0)
3041 {
3042 hr = InitILPdbData();
3043 if (FAILED(hr))
3044 return hr;
3045 isILPDBProvided = true;
3046 }
3047
3048 // Create the PDB file we will write into.
3049
3050 _ASSERTE(m_Create != NULL);
3051 _ASSERTE(m_pModule != NULL);
3052
3053 PEImageLayout * pLoadedLayout = m_pModule->GetFile()->GetLoaded();
3054
3055 // Currently DiaSymReader does not work properly generating NGEN PDBS unless
3056 // the DLL whose PDB is being generated ends in .ni.*. Unfortunately, readyToRun
3057 // images do not follow this convention and end up producing bad PDBS. To fix
3058 // this (without changing diasymreader.dll which ships indepdendently of .Net Core)
3059 // we copy the file to somethign with this convention before generating the PDB
3060 // and delete it when we are done.
3061 SString dllPath = pLoadedLayout->GetPath();
3062 if (!dllPath.EndsWithCaseInsensitive(W(".ni.dll")) && !dllPath.EndsWithCaseInsensitive(W(".ni.exe")))
3063 {
3064 SString::Iterator fileNameStart = dllPath.End();
3065 dllPath.FindBack(fileNameStart, DIRECTORY_SEPARATOR_STR_W);
3066
3067 SString::Iterator ext = dllPath.End();
3068 dllPath.FindBack(ext, '.');
3069
3070 // m_tempSourceDllName = Convertion of INPUT.dll to INPUT.ni.dll where the PDB lives.
3071 m_tempSourceDllName = m_wszPdbPath;
3072 m_tempSourceDllName += SString(dllPath, fileNameStart, ext - fileNameStart);
3073 m_tempSourceDllName += W(".ni");
3074 m_tempSourceDllName += SString(dllPath, ext, dllPath.End() - ext);
3075 CopyFileW(dllPath, m_tempSourceDllName, false);
3076 dllPath = m_tempSourceDllName;
3077 }
3078
3079 ReleaseHolder<ISymNGenWriter> pWriter1;
3080 hr = m_Create(dllPath, m_wszPdbPath, &pWriter1);
3081 if (FAILED(hr))
3082 return hr;
3083
3084 hr = pWriter1->QueryInterface(IID_ISymNGenWriter2, (LPVOID*) &m_pWriter);
3085 if (FAILED(hr))
3086 {
3087 GetSvcLogger()->Printf(
3088 W("An incorrect version of diasymreader.dll was found. Please ensure that version 11 or greater of diasymreader.dll is on the path. You can typically find this DLL in the desktop .NET install directory for 4.5 or greater. Error='0x%x'\n"),
3089 hr);
3090 return hr;
3091 }
3092
3093 // PDB file is now created. Get its path and initialize the holder so the PDB file
3094 // can be deleted if we don't make it successfully to the end
3095
3096 hr = m_pWriter->QueryPDBNameExW(m_wszPDBFilePath, _countof(m_wszPDBFilePath));
3097 if (SUCCEEDED(hr))
3098 {
3099 // A failure in QueryPDBNameExW above isn't fatal--it just means we can't
3100 // initialize m_deletePDBFileHolder, and thus may leave the PDB file on disk if
3101 // there's *another* error later on. And if we do hit another error, NGEN will
3102 // still return an error exit code, so the worst we'll have is a bogus PDB file
3103 // that no one should expect works anyway.
3104 m_deletePDBFileHolder.Assign(m_wszPDBFilePath);
3105 }
3106
3107
3108 hr = m_pdbMod.Open(m_pWriter, pLoadedLayout->GetPath(), m_pModule->GetPath());
3109 if (FAILED(hr))
3110 return hr;
3111
3112 hr = WriteStringTable();
3113 if (FAILED(hr))
3114 return hr;
3115
3116 hr = WriteFileChecksums();
3117 if (FAILED(hr))
3118 return hr;
3119
3120
3121 COUNT_T sectionCount = pLoadedLayout->GetNumberOfSections();
3122 IMAGE_SECTION_HEADER *section = pLoadedLayout->FindFirstSection();
3123 COUNT_T sectionIndex = 0;
3124 USHORT iCodeSection = 0;
3125 BYTE *pCodeBase = NULL;
3126 while (sectionIndex < sectionCount)
3127 {
3128 hr = m_pWriter->AddSection((USHORT)(sectionIndex + 1),
3129 OMF_StandardText,
3130 0,
3131 section[sectionIndex].SizeOfRawData);
3132 if (FAILED(hr))
3133 return hr;
3134
3135 if (strcmp((const char *)&section[sectionIndex].Name[0], ".text") == 0) {
3136 _ASSERTE((iCodeSection == 0) && (pCodeBase == NULL));
3137 iCodeSection = (USHORT)(sectionIndex + 1);
3138 pCodeBase = (BYTE *)section[sectionIndex].VirtualAddress;
3139 }
3140
3141 // In order to support the DIA RVA-to-lines API against the PDB we're
3142 // generating, we need to update the section contribution table with each
3143 // section we add.
3144 hr = m_pWriter->ModAddSecContribEx(
3145 m_pdbMod.GetModPtr(),
3146 (USHORT)(sectionIndex + 1),
3147 0,
3148 section[sectionIndex].SizeOfRawData,
3149 section[sectionIndex].Characteristics,
3150 0, // dwDataCrc
3151 0 // dwRelocCrc
3152 );
3153 if (FAILED(hr))
3154 return hr;
3155
3156 sectionIndex++;
3157 }
3158
3159 _ASSERTE(iCodeSection != 0);
3160 _ASSERTE(pCodeBase != NULL);
3161
3162
3163 // To support lines info, we need a "dummy" section, indexed as 0, for use as a
3164 // sentinel when MSPDB sets up its section contribution table
3165 hr = m_pWriter->AddSection(0, // Dummy section 0
3166 OMF_SentinelType,
3167 0,
3168 0xFFFFffff);
3169 if (FAILED(hr))
3170 return hr;
3171
3172
3173#ifdef FEATURE_READYTORUN_COMPILER
3174 if (pLoadedLayout->HasReadyToRunHeader())
3175 {
3176 ReadyToRunInfo::MethodIterator mi(m_pModule->GetReadyToRunInfo());
3177 while (mi.Next())
3178 {
3179 MethodDesc *hotDesc = mi.GetMethodDesc();
3180
3181 hr = WriteMethodPDBData(pLoadedLayout, iCodeSection, pCodeBase, hotDesc, mi.GetMethodStartAddress(), isILPDBProvided);
3182 if (FAILED(hr))
3183 return hr;
3184 }
3185 }
3186 else
3187#endif // FEATURE_READYTORUN_COMPILER
3188 {
3189 MethodIterator mi(m_pModule);
3190 while (mi.Next())
3191 {
3192 MethodDesc *hotDesc = mi.GetMethodDesc();
3193 hotDesc->CheckRestore();
3194
3195 hr = WriteMethodPDBData(pLoadedLayout, iCodeSection, pCodeBase, hotDesc, mi.GetMethodStartAddress(), isILPDBProvided);
3196 if (FAILED(hr))
3197 return hr;
3198 }
3199 }
3200
3201 // We made it successfully to the end, so don't delete the PDB file.
3202 m_deletePDBFileHolder.SuppressRelease();
3203 return S_OK;
3204}
3205
3206HRESULT NGenModulePdbWriter::WriteMethodPDBData(PEImageLayout * pLoadedLayout, USHORT iCodeSection, BYTE *pCodeBase, MethodDesc * hotDesc, PCODE start, bool isILPDBProvided)
3207{
3208 STANDARD_VM_CONTRACT;
3209
3210 HRESULT hr;
3211
3212 EECodeInfo codeInfo(start);
3213 _ASSERTE(codeInfo.IsValid());
3214
3215 IJitManager::MethodRegionInfo methodRegionInfo;
3216 codeInfo.GetMethodRegionInfo(&methodRegionInfo);
3217
3218 PCODE pHotCodeStart = methodRegionInfo.hotStartAddress;
3219 _ASSERTE(pHotCodeStart);
3220
3221 PCODE pColdCodeStart = methodRegionInfo.coldStartAddress;
3222 SString mAssemblyName;
3223 mAssemblyName.SetUTF8(m_pModule->GetAssembly()->GetSimpleName());
3224 SString assemblyName;
3225 assemblyName.SetUTF8(hotDesc->GetAssembly()->GetSimpleName());
3226 SString methodToken;
3227 methodToken.Printf("%X", hotDesc->GetMemberDef());
3228
3229 // Hot name
3230 {
3231 SString fullName;
3232 TypeString::AppendMethodInternal(
3233 fullName,
3234 hotDesc,
3235 TypeString::FormatNamespace | TypeString::FormatSignature);
3236 fullName.Append(W("$#"));
3237 if (!mAssemblyName.Equals(assemblyName))
3238 fullName.Append(assemblyName);
3239 fullName.Append(W("#"));
3240 fullName.Append(methodToken);
3241 BSTRHolder hotNameHolder(SysAllocString(fullName.GetUnicode()));
3242 hr = m_pWriter->AddSymbol(hotNameHolder,
3243 iCodeSection,
3244 (pHotCodeStart - (TADDR)pLoadedLayout->GetBase() - (TADDR)pCodeBase));
3245 if (FAILED(hr))
3246 return hr;
3247 }
3248
3249 // Cold name
3250 {
3251 if (pColdCodeStart) {
3252
3253 SString fullNameCold;
3254 fullNameCold.Append(W("[COLD] "));
3255 TypeString::AppendMethodInternal(
3256 fullNameCold,
3257 hotDesc,
3258 TypeString::FormatNamespace | TypeString::FormatSignature);
3259 fullNameCold.Append(W("$#"));
3260 if (!mAssemblyName.Equals(assemblyName))
3261 fullNameCold.Append(assemblyName);
3262 fullNameCold.Append(W("#"));
3263 fullNameCold.Append(methodToken);
3264
3265 BSTRHolder coldNameHolder(SysAllocString(fullNameCold.GetUnicode()));
3266 hr = m_pWriter->AddSymbol(coldNameHolder,
3267 iCodeSection,
3268 (pColdCodeStart - (TADDR)pLoadedLayout->GetBase() - (TADDR)pCodeBase));
3269
3270 if (FAILED(hr))
3271 return hr;
3272
3273 }
3274 }
3275
3276 // Offset / lines mapping
3277 // Skip functions that are too big for PDB lines format
3278 if (FitsIn<DWORD>(methodRegionInfo.hotSize) &&
3279 FitsIn<DWORD>(methodRegionInfo.coldSize))
3280 {
3281 NGenMethodLinesPdbWriter methodLinesWriter(
3282 m_pWriter,
3283 m_pdbMod.GetModPtr(),
3284 m_pReader,
3285 hotDesc,
3286 start,
3287 iCodeSection,
3288 (TADDR)pLoadedLayout->GetBase() + (TADDR)pCodeBase,
3289 &methodRegionInfo,
3290 &codeInfo,
3291 &m_docNameToOffsetMap,
3292 isILPDBProvided);
3293
3294 hr = methodLinesWriter.WritePDBData();
3295 if (FAILED(hr))
3296 return hr;
3297 }
3298
3299 return S_OK;
3300}
3301
3302// ----------------------------------------------------------------------------
3303// Handles writing the file checksums subsection to the PDB
3304//
3305HRESULT NGenModulePdbWriter::WriteFileChecksums()
3306{
3307 STANDARD_VM_CONTRACT;
3308
3309 _ASSERTE(m_pWriter != NULL);
3310
3311 // The file checksums subsection of the PDB (i.e., "DEBUG_S_FILECHKSMS"), is a blob
3312 // consisting of a few structs stacked one after the other:
3313 //
3314 // * (1) DWORD = CV_SIGNATURE_C13 -- the usual subsection signature DWORD
3315 // * (2) CV_DebugSSubsectionHeader_t -- the usual subsection header, with type =
3316 // DEBUG_S_FILECHKSMS
3317 // * (3) Blob consisting of an array of checksum data -- the format of this piece is
3318 // not defined via structs (not sure why), but is defined in
3319 // vctools\PDB\doc\lines.docx
3320 //
3321 HRESULT hr;
3322
3323 // PDB format requires that the checksum size can always be expressed in a BYTE.
3324 const BYTE kcbEachChecksumEstimate = 0xFF;
3325
3326 UINT64 cbChecksumSubsectionEstimate =
3327 sizeof(DWORD) +
3328 sizeof(CV_DebugSSubsectionHeader_t) +
3329 m_finalPdbDocCount * kcbEachChecksumEstimate;
3330 if (!FitsIn<ULONG32>(cbChecksumSubsectionEstimate))
3331 {
3332 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
3333 }
3334
3335 NewArrayHolder<BYTE> rgbChksumSubsection(new BYTE[ULONG32(cbChecksumSubsectionEstimate)]);
3336 LPBYTE pbChksumSubsectionCur = rgbChksumSubsection;
3337
3338 // (1) Subsection signature
3339 *((DWORD *) pbChksumSubsectionCur) = CV_SIGNATURE_C13;
3340 pbChksumSubsectionCur += sizeof(DWORD);
3341
3342 // (2) Subsection header
3343 CV_DebugSSubsectionHeader_t * pSubSectHeader = (CV_DebugSSubsectionHeader_t *) pbChksumSubsectionCur;
3344 memset(pSubSectHeader, 0, sizeof(*pSubSectHeader));
3345 pSubSectHeader->type = DEBUG_S_FILECHKSMS;
3346 pbChksumSubsectionCur += sizeof(*pSubSectHeader);
3347 // pSubSectHeader->cblen to be filled in later once we know the size
3348
3349 LPBYTE pbChksumDataStart = pbChksumSubsectionCur;
3350
3351 // (3) Iterate through source files, steal their checksum info from the IL PDB, and
3352 // write it into the NGEN PDB.
3353 for (ULONG32 i = 0; i < m_finalPdbDocCount; i++)
3354 {
3355 WCHAR wszURL[MAX_LONGPATH] = UNKNOWN_SOURCE_FILE_PATH;
3356 char szURL[MAX_LONGPATH];
3357 ULONG32 cchURL;
3358
3359
3360 bool isKnownSourcePath = i < m_ilPdbDocCount;
3361 if (isKnownSourcePath)
3362 {
3363 // For NGenMethodLinesPdbWriter::WriteDebugSILLinesSubsection, we won't write the path info.
3364 // In order to let WriteDebugSILLinesSubsection can find "UNKNOWN_SOURCE_FILE_PATH" which is
3365 // not existed in m_rgpDocs, no matter we have IL PDB or not, we let m_finalPdbDocCount equals to
3366 // m_ilPdbDocCount + 1 and write the extra one path as "UNKNOWN_SOURCE_FILE_PATH". That also explains
3367 // why we have a inconsistence between m_finalPdbDocCount and m_ilPdbDocCount.
3368 hr = m_rgpDocs[i]->GetURL(_countof(wszURL), &cchURL, wszURL);
3369 if (FAILED(hr))
3370 return hr;
3371 }
3372
3373 int cbWritten = WideCharToMultiByte(
3374 CP_UTF8,
3375 0, // dwFlags
3376 wszURL,
3377 -1, // i.e., input is NULL-terminated
3378 szURL, // output: UTF8 string starts here
3379 _countof(szURL), // Available space
3380 NULL, // lpDefaultChar
3381 NULL // lpUsedDefaultChar
3382 );
3383 if (cbWritten == 0)
3384 return HRESULT_FROM_WIN32(GetLastError());
3385
3386 // find offset into string table and add to blob; meanwhile update hash to
3387 // remember the offset into the cksum table
3388 const KeyValuePair<LPCSTR,DocNameOffsets> * pMapEntry =
3389 m_docNameToOffsetMap.LookupPtr(szURL);
3390 if (pMapEntry == NULL)
3391 {
3392 // Should never happen, as it implies we found a source file that was never
3393 // written to the string table
3394 return E_UNEXPECTED;
3395 }
3396 DocNameOffsets docNameOffsets(pMapEntry->Value());
3397 docNameOffsets.m_dwChksumTableOffset = ULONG32(pbChksumSubsectionCur - pbChksumDataStart);
3398
3399 // Update the map with the new docNameOffsets that contains the cksum table
3400 // offset as well. Note that we must ensure the key (LPCSTR) remains the same
3401 // (thus we explicitly ask for the Key()). This class guarantees that string
3402 // pointer (which comes from the string table buffer field) will remain allocated
3403 // as long as the map is.
3404 m_docNameToOffsetMap.AddOrReplace(pMapEntry->Key(), docNameOffsets);
3405 * (ULONG32 *) pbChksumSubsectionCur = docNameOffsets.m_dwStrTableOffset;
3406 pbChksumSubsectionCur += sizeof(ULONG32);
3407
3408 // Checksum algorithm and bytes
3409
3410 BYTE rgbChecksum[kcbEachChecksumEstimate];
3411 ULONG32 cbChecksum = 0;
3412 BYTE bChecksumAlgorithmType = CHKSUM_TYPE_NONE;
3413 if (isKnownSourcePath)
3414 {
3415 GUID guidChecksumAlgorithm;
3416 hr = m_rgpDocs[i]->GetCheckSumAlgorithmId(&guidChecksumAlgorithm);
3417 if (SUCCEEDED(hr))
3418 {
3419 // If we got the checksum algorithm, we can write it all out to the buffer.
3420 // Else, we'll just omit the checksum info
3421 if (memcmp(&guidChecksumAlgorithm, &CorSym_SourceHash_MD5, sizeof(GUID)) == 0)
3422 bChecksumAlgorithmType = CHKSUM_TYPE_MD5;
3423 else if (memcmp(&guidChecksumAlgorithm, &CorSym_SourceHash_SHA1, sizeof(GUID)) == 0)
3424 bChecksumAlgorithmType = CHKSUM_TYPE_SHA1;
3425 }
3426 }
3427
3428 if (bChecksumAlgorithmType != CHKSUM_TYPE_NONE)
3429 {
3430 hr = m_rgpDocs[i]->GetCheckSum(sizeof(rgbChecksum), &cbChecksum, rgbChecksum);
3431 if (FAILED(hr) || !FitsIn<BYTE>(cbChecksum))
3432 {
3433 // Should never happen, but just in case checksum data is invalid, just put
3434 // no checksum into the NGEN PDB
3435 bChecksumAlgorithmType = CHKSUM_TYPE_NONE;
3436 cbChecksum = 0;
3437 }
3438 }
3439
3440 // checksum length & algorithm
3441 *pbChksumSubsectionCur = (BYTE) cbChecksum;
3442 pbChksumSubsectionCur++;
3443 *pbChksumSubsectionCur = bChecksumAlgorithmType;
3444 pbChksumSubsectionCur++;
3445
3446 // checksum data bytes
3447 memcpy(pbChksumSubsectionCur, rgbChecksum, cbChecksum);
3448 pbChksumSubsectionCur += cbChecksum;
3449
3450 // Must align to the next 4-byte boundary
3451 LPBYTE pbChksumSubsectionCurAligned = (LPBYTE) ALIGN_UP(pbChksumSubsectionCur, 4);
3452 memset(pbChksumSubsectionCur, 0, pbChksumSubsectionCurAligned-pbChksumSubsectionCur);
3453 pbChksumSubsectionCur = pbChksumSubsectionCurAligned;
3454 }
3455
3456 // Now that we know pSubSectHeader->cbLen, fill it in
3457 pSubSectHeader->cbLen = CV_off32_t(pbChksumSubsectionCur - pbChksumDataStart);
3458
3459 // Subsection is now filled out, so add it
3460 hr = m_pWriter->ModAddSymbols(
3461 m_pdbMod.GetModPtr(),
3462 rgbChksumSubsection,
3463 int(pbChksumSubsectionCur - rgbChksumSubsection));
3464 if (FAILED(hr))
3465 return hr;
3466
3467 return S_OK;
3468}
3469
3470// ----------------------------------------------------------------------------
3471// NGenMethodLinesPdbWriter implementation
3472
3473
3474//---------------------------------------------------------------------------------------
3475//
3476// Manages the writing of all lines-file subsections requred for a given method. if a
3477// method is hot/cold split, this will write two line-file subsections to the PDB--one
3478// for the hot region, and one for the cold.
3479//
3480
3481HRESULT NGenMethodLinesPdbWriter::WritePDBData()
3482{
3483 STANDARD_VM_CONTRACT;
3484
3485 if (m_hotDesc->IsNoMetadata())
3486 {
3487 // IL stubs will not have data in the IL PDB, so just skip them.
3488 return S_OK;
3489 }
3490
3491 //
3492 // First, we'll need to merge the IL-to-native map from the JIT manager with the
3493 // IL-to-source map from the IL PDB. This merging is done into a single piece that
3494 // includes all regions of the code when it's split
3495 //
3496
3497 // Grab the IL-to-native map from the JIT manager
3498 DebugInfoRequest debugInfoRequest;
3499 debugInfoRequest.InitFromStartingAddr(m_hotDesc, m_start);
3500 BOOL fSuccess = m_pCodeInfo->GetJitManager()->GetBoundariesAndVars(
3501 debugInfoRequest,
3502 SimpleNew, NULL, // Allocator
3503 &m_cIlNativeMap,
3504 &m_rgIlNativeMap,
3505 NULL, NULL);
3506 if (!fSuccess)
3507 {
3508 // Shouldn't happen, but just skip this method if it does
3509 return S_OK;
3510 }
3511 HRESULT hr;
3512 if (FAILED(hr = WriteNativeILMapPDBData()))
3513 {
3514 return hr;
3515 }
3516
3517 if (!m_isILPDBProvided)
3518 {
3519 return S_OK;
3520 }
3521
3522 // We will traverse this IL-to-native map (from the JIT) in parallel with the
3523 // source-to-IL map provided by the IL PDB (below). Both need to be sorted by IL so
3524 // we can easily find matching entries in the two maps
3525 QuickSortILNativeMapByIL sorterByIl(m_rgIlNativeMap, m_cIlNativeMap);
3526 sorterByIl.Sort();
3527
3528 // Now grab IL-to-source map from the IL PDBs (just known as "sequence points"
3529 // according to the IL PDB API)
3530
3531 ReleaseHolder<ISymUnmanagedMethod> pMethod;
3532 hr = m_pReader->GetMethod(
3533 m_hotDesc->GetMemberDef(),
3534 &pMethod);
3535 if (FAILED(hr))
3536 {
3537 // Ignore any methods not included in the IL PDB. Although we've already
3538 // excluded LCG & IL stubs from methods we're considering, there can still be
3539 // methods in the NGEN module that are not in the IL PDB (e.g., implicit ctors).
3540 return S_OK;
3541 }
3542
3543 ULONG32 cSeqPointsExpected;
3544 hr = pMethod->GetSequencePointCount(&cSeqPointsExpected);
3545 if (FAILED(hr))
3546 {
3547 // Should never happen, but we can just skip this function if the IL PDB can't
3548 // find sequence point info
3549 return S_OK;
3550 }
3551
3552 ULONG32 cSeqPointsReturned;
3553 m_rgilOffsets = new ULONG32[cSeqPointsExpected];
3554 m_rgpDocs = new ISymUnmanagedDocument * [cSeqPointsExpected];
3555 m_rgnLineStarts = new ULONG32[cSeqPointsExpected];
3556
3557 // This is guaranteed to return the sequence points sorted in order of the IL
3558 // offsets (m_rgilOffsets)
3559 hr = pMethod->GetSequencePoints(
3560 cSeqPointsExpected,
3561 &cSeqPointsReturned,
3562 m_rgilOffsets,
3563 m_rgpDocs,
3564 m_rgnLineStarts,
3565 NULL, // ColumnStarts not needed
3566 NULL, // LineEnds not needed
3567 NULL); // ColumnEnds not needed
3568 if (FAILED(hr))
3569 {
3570 // Shouldn't happen, but just skip this method if it does
3571 return S_OK;
3572 }
3573 // Commit m_rgpDocs to calling Release() on all ISymUnmanagedDocument* returned into
3574 // the array.
3575 m_rgpDocs.SetElementCount(cSeqPointsReturned);
3576
3577 // Now merge the two maps together into an array of MapIndexPair structures. Traverse
3578 // both maps in parallel (both ordered by IL offset), looking for IL offset matches.
3579 // Range matching: If an entry in the IL-to-native map has no matching entry in the
3580 // IL PDB, then seek up in the IL PDB to the previous sequence point and merge to
3581 // that (assuming that previous sequence point from the IL PDB did not already have
3582 // an exact match to some other entry in the IL-to-native map).
3583 ULONG32 cMapIndexPairsMax = m_cIlNativeMap;
3584 NewArrayHolder<MapIndexPair> rgMapIndexPairs(new MapIndexPair [cMapIndexPairsMax]);
3585 ULONG32 iSeqPoints = 0;
3586
3587 // Keep track (via iSeqPointLastUnmatched) of the most recent entry in the IL PDB
3588 // that we passed over because it had no matching entry in the IL-to-native map. We
3589 // may use this to do a range-match if necessary. We'll set iSeqPointLastUnmatched to
3590 // the currently interated IL PDB entry after our cursor in the il-to-native map
3591 // passed it by, but only if fCurSeqPointMatched is FALSE
3592 ULONG32 iSeqPointLastUnmatched = (ULONG32) -1;
3593 BOOL fCurSeqPointMatched = FALSE;
3594
3595 ULONG32 iIlNativeMap = 0;
3596 ULONG32 iMapIndexPairs = 0;
3597
3598 // Traverse IL PDB entries and IL-to-native map entries (both sorted by IL) in
3599 // parallel
3600 //
3601 // * Record matching indices in our output map, rgMapIndexPairs, indexed by
3602 // iMapIndexPairs.
3603 //
3604 // * We will have at most m_cIlNativeMap entries in rgMapIndexPairs by the time
3605 // we're done. (Each il-to-native map entry will be considered for inclusion
3606 // in this output. Those il-to-native map entries with a match in the il PDB
3607 // will be included, the rest skipped.)
3608 //
3609 // * iSeqPointLastUnmatched != -1 iff it equals a prior entry in the IL PDB that
3610 // we skipped over because it could not be exactly matched to an entry in the
3611 // il-to-native map. In such a case, it will be considered for a
3612 // range-match to the next il-to-native map entry
3613 while (iIlNativeMap < m_cIlNativeMap)
3614 {
3615 _ASSERTE (iMapIndexPairs < cMapIndexPairsMax);
3616
3617 // IP addresses that map to "special" places (prolog, epilog, or
3618 // other hidden code), will just map to 0xFeeFee, as per convention
3619 if ((m_rgIlNativeMap[iIlNativeMap].ilOffset == NO_MAPPING) ||
3620 (m_rgIlNativeMap[iIlNativeMap].ilOffset == PROLOG) ||
3621 (m_rgIlNativeMap[iIlNativeMap].ilOffset == EPILOG))
3622 {
3623 rgMapIndexPairs[iMapIndexPairs].m_iIlNativeMap = iIlNativeMap;
3624 rgMapIndexPairs[iMapIndexPairs].m_iSeqPoints = kUnmappedIP;
3625 iMapIndexPairs++;
3626
3627 // If we were remembering a prior unmatched entry in the IL PDB, reset it
3628 iSeqPointLastUnmatched = (ULONG32) -1;
3629
3630 // Advance il-native map, NOT il-source map
3631 iIlNativeMap++;
3632 continue;
3633 }
3634
3635 // Cases below actually look at the IL PDB sequence point, so ensure it's still
3636 // in range; otherwise, we're done.
3637 if (iSeqPoints >= cSeqPointsReturned)
3638 break;
3639
3640 if (m_rgIlNativeMap[iIlNativeMap].ilOffset < m_rgilOffsets[iSeqPoints])
3641 {
3642 // Our cursor over the ilnative map is behind the sourceil
3643 // map
3644
3645 if (iSeqPointLastUnmatched != (ULONG32) -1)
3646 {
3647 // Range matching: This ilnative entry is behind our cursor in the
3648 // sourceil map, but this ilnative entry is also ahead of the previous
3649 // (unmatched) entry in the sourceil map. So this is a case where the JIT
3650 // generated sequence points that surround, without matching, that
3651 // previous entry in the sourceil map. So match to that previous
3652 // (unmatched) entry in the sourceil map.
3653 _ASSERTE(m_rgilOffsets[iSeqPointLastUnmatched] < m_rgIlNativeMap[iIlNativeMap].ilOffset);
3654 rgMapIndexPairs[iMapIndexPairs].m_iIlNativeMap = iIlNativeMap;
3655 rgMapIndexPairs[iMapIndexPairs].m_iSeqPoints = iSeqPointLastUnmatched;
3656 iMapIndexPairs++;
3657
3658 // Reset our memory of the last unmatched entry in the IL PDB
3659 iSeqPointLastUnmatched = (ULONG32) -1;
3660 }
3661 else if (iMapIndexPairs > 0)
3662 {
3663 DWORD lastMatchedilNativeIndex = rgMapIndexPairs[iMapIndexPairs - 1].m_iIlNativeMap;
3664 if (m_rgIlNativeMap[iIlNativeMap].ilOffset == m_rgIlNativeMap[lastMatchedilNativeIndex].ilOffset &&
3665 m_rgIlNativeMap[iIlNativeMap].nativeOffset < m_rgIlNativeMap[lastMatchedilNativeIndex].nativeOffset)
3666 {
3667 rgMapIndexPairs[iMapIndexPairs - 1].m_iIlNativeMap = iIlNativeMap;
3668 }
3669
3670 }
3671 // Go to next ilnative map entry
3672 iIlNativeMap++;
3673 continue;
3674 }
3675
3676 if (m_rgilOffsets[iSeqPoints] < m_rgIlNativeMap[iIlNativeMap].ilOffset)
3677 {
3678 // Our cursor over the ilnative map is ahead of the sourceil
3679 // map, so go to next sourceil map entry. Remember that we're passing over
3680 // this entry in the sourceil map, in case we choose to match to it later.
3681 if (!fCurSeqPointMatched)
3682 {
3683 iSeqPointLastUnmatched = iSeqPoints;
3684 }
3685 iSeqPoints++;
3686 fCurSeqPointMatched = FALSE;
3687 continue;
3688 }
3689
3690 // At a match
3691 _ASSERTE(m_rgilOffsets[iSeqPoints] == m_rgIlNativeMap[iIlNativeMap].ilOffset);
3692 rgMapIndexPairs[iMapIndexPairs].m_iIlNativeMap = iIlNativeMap;
3693 rgMapIndexPairs[iMapIndexPairs].m_iSeqPoints = iSeqPoints;
3694
3695 // If we were remembering a prior unmatched entry in the IL PDB, reset it
3696 iSeqPointLastUnmatched = (ULONG32) -1;
3697
3698 // Advance il-native map, do not advance il-source map in case the next il-native
3699 // entry matches this current il-source map entry, but remember that this current
3700 // il-source map entry has found an exact match
3701 iMapIndexPairs++;
3702 iIlNativeMap++;
3703 fCurSeqPointMatched = TRUE;
3704 }
3705
3706 ULONG32 cMapIndexPairs = iMapIndexPairs;
3707
3708 // PDB format requires the lines array to be sorted by IP offset
3709 QuickSortMapIndexPairsByNativeOffset sorterByIp(rgMapIndexPairs, cMapIndexPairs, m_rgIlNativeMap, m_cIlNativeMap);
3710 sorterByIp.Sort();
3711
3712 //
3713 // Now that the maps are merged and sorted, determine whether there's a hot/cold
3714 // split, where that split is, and then call WriteLinesSubsection to write out each
3715 // region into its own lines-file subsection
3716 //
3717
3718 // Find the point where the code got split
3719 ULONG32 iMapIndexPairsFirstEntryInColdSection = cMapIndexPairs;
3720 for (iMapIndexPairs = 0; iMapIndexPairs < cMapIndexPairs; iMapIndexPairs++)
3721 {
3722 DWORD dwNativeOffset = m_rgIlNativeMap[rgMapIndexPairs[iMapIndexPairs].m_iIlNativeMap].nativeOffset;
3723 if (dwNativeOffset >= m_pMethodRegionInfo->hotSize)
3724 {
3725 iMapIndexPairsFirstEntryInColdSection = iMapIndexPairs;
3726 break;
3727 }
3728 }
3729
3730 // Adjust the cold offsets (if any) to be relative to the cold start
3731 for (iMapIndexPairs = iMapIndexPairsFirstEntryInColdSection; iMapIndexPairs < cMapIndexPairs; iMapIndexPairs++)
3732 {
3733 DWORD dwNativeOffset = m_rgIlNativeMap[rgMapIndexPairs[iMapIndexPairs].m_iIlNativeMap].nativeOffset;
3734 _ASSERTE (dwNativeOffset >= m_pMethodRegionInfo->hotSize);
3735
3736 // Adjust offset so it's relative to the cold region start
3737 dwNativeOffset -= DWORD(m_pMethodRegionInfo->hotSize);
3738 _ASSERTE(dwNativeOffset < m_pMethodRegionInfo->coldSize);
3739 m_rgIlNativeMap[rgMapIndexPairs[iMapIndexPairs].m_iIlNativeMap].nativeOffset = dwNativeOffset;
3740 }
3741
3742 // Write out the hot region into its own lines-file subsection
3743 hr = WriteDebugSLinesSubsection(
3744 ULONG32(m_pMethodRegionInfo->hotStartAddress - m_addrCodeSection),
3745 ULONG32(m_pMethodRegionInfo->hotSize),
3746 rgMapIndexPairs,
3747 iMapIndexPairsFirstEntryInColdSection);
3748 if (FAILED(hr))
3749 return hr;
3750
3751 // If there was a hot/cold split, write a separate lines-file subsection for the cold
3752 // region
3753 if (iMapIndexPairsFirstEntryInColdSection < cMapIndexPairs)
3754 {
3755 hr = WriteDebugSLinesSubsection(
3756 ULONG32(m_pMethodRegionInfo->coldStartAddress - m_addrCodeSection),
3757 ULONG32(m_pMethodRegionInfo->coldSize),
3758 &rgMapIndexPairs[iMapIndexPairsFirstEntryInColdSection],
3759 cMapIndexPairs - iMapIndexPairsFirstEntryInColdSection);
3760 if (FAILED(hr))
3761 return hr;
3762 }
3763
3764 return S_OK;
3765}
3766
3767//---------------------------------------------------------------------------------------
3768//
3769// Manages the writing of all native-IL subsections requred for a given method. Almost do
3770// the same thing as NGenMethodLinesPdbWriter::WritePDBData. But we will write the native-IL
3771// map this time.
3772//
3773
3774HRESULT NGenMethodLinesPdbWriter::WriteNativeILMapPDBData()
3775{
3776 STANDARD_VM_CONTRACT;
3777
3778 HRESULT hr;
3779
3780 QuickSortILNativeMapByNativeOffset sorterByNativeOffset(m_rgIlNativeMap, m_cIlNativeMap);
3781 sorterByNativeOffset.Sort();
3782
3783 ULONG32 iIlNativeMap = 0;
3784 ULONG32 ilNativeMapFirstEntryInColdeSection = m_cIlNativeMap;
3785 for (iIlNativeMap = 0; iIlNativeMap < m_cIlNativeMap; iIlNativeMap++)
3786 {
3787 if (m_rgIlNativeMap[iIlNativeMap].nativeOffset >= m_pMethodRegionInfo->hotSize)
3788 {
3789 ilNativeMapFirstEntryInColdeSection = iIlNativeMap;
3790 break;
3791 }
3792 }
3793
3794 NewArrayHolder<ICorDebugInfo::OffsetMapping> coldRgIlNativeMap(new ICorDebugInfo::OffsetMapping[m_cIlNativeMap - ilNativeMapFirstEntryInColdeSection]);
3795 // Adjust the cold offsets (if any) to be relative to the cold start
3796 for (iIlNativeMap = ilNativeMapFirstEntryInColdeSection; iIlNativeMap < m_cIlNativeMap; iIlNativeMap++)
3797 {
3798 DWORD dwNativeOffset = m_rgIlNativeMap[iIlNativeMap].nativeOffset;
3799 _ASSERTE(dwNativeOffset >= m_pMethodRegionInfo->hotSize);
3800
3801 // Adjust offset so it's relative to the cold region start
3802 dwNativeOffset -= DWORD(m_pMethodRegionInfo->hotSize);
3803 _ASSERTE(dwNativeOffset < m_pMethodRegionInfo->coldSize);
3804 coldRgIlNativeMap[iIlNativeMap - ilNativeMapFirstEntryInColdeSection].ilOffset = m_rgIlNativeMap[iIlNativeMap].ilOffset;
3805 coldRgIlNativeMap[iIlNativeMap - ilNativeMapFirstEntryInColdeSection].nativeOffset = dwNativeOffset;
3806 coldRgIlNativeMap[iIlNativeMap - ilNativeMapFirstEntryInColdeSection].source = m_rgIlNativeMap[iIlNativeMap].source;
3807 }
3808
3809 // Write out the hot region into its own lines-file subsection
3810 hr = WriteDebugSILLinesSubsection(
3811 ULONG32(m_pMethodRegionInfo->hotStartAddress - m_addrCodeSection),
3812 ULONG32(m_pMethodRegionInfo->hotSize),
3813 m_rgIlNativeMap,
3814 ilNativeMapFirstEntryInColdeSection);
3815 if (FAILED(hr))
3816 return hr;
3817
3818 // If there was a hot/cold split, write a separate lines-file subsection for the cold
3819 // region
3820 if (ilNativeMapFirstEntryInColdeSection < m_cIlNativeMap)
3821 {
3822 hr = WriteDebugSILLinesSubsection(
3823 ULONG32(m_pMethodRegionInfo->coldStartAddress - m_addrCodeSection),
3824 ULONG32(m_pMethodRegionInfo->coldSize),
3825 coldRgIlNativeMap,
3826 m_cIlNativeMap - ilNativeMapFirstEntryInColdeSection);
3827 if (FAILED(hr))
3828 return hr;
3829 }
3830
3831 return S_OK;
3832}
3833
3834
3835//---------------------------------------------------------------------------------------
3836//
3837// Helper called by NGenMethodLinesPdbWriter::WriteDebugSLinesSubsection and
3838// NGenMethodLinesPdbWriter::WriteDebugSILLinesSubsection to initial the DEBUG_S*_LINE
3839// subsection headers.
3840//
3841// Arguments:
3842// * ulCodeStartOffset - Offset relative to the code section, or where this region
3843// of code begins
3844// * type - the subsection's type
3845// * lineSize - how many lines mapping the subsection will have.
3846// * cbCode - Size in bytes of this region of code
3847// * ppSubSectHeader - output value which returns the intialed CV_DebugSLinesHeader_t struct pointer.
3848// * ppLinesHeader - output value which returns the initialed CV_DebugSLinesHeader_t struct pointer.
3849// * ppbLinesSubsectionCur - output value which points to the address right after the DebugSLinesHeader
3850//
3851// Return Value:
3852// * Pointer which points the staring address of the SubSection.
3853//
3854
3855LPBYTE NGenMethodLinesPdbWriter::InitDebugLinesHeaderSection(
3856 DEBUG_S_SUBSECTION_TYPE type,
3857 ULONG32 ulCodeStartOffset,
3858 ULONG32 cbCode,
3859 ULONG32 lineSize,
3860 CV_DebugSSubsectionHeader_t **ppSubSectHeader /*out*/,
3861 CV_DebugSLinesHeader_t ** ppLinesHeader /*out*/,
3862 LPBYTE * ppbLinesSubsectionCur /*out*/)
3863{
3864 STANDARD_VM_CONTRACT;
3865
3866 UINT64 cbLinesSubsectionEstimate =
3867 sizeof(DWORD) +
3868 sizeof(CV_DebugSSubsectionHeader_t) +
3869 sizeof(CV_DebugSLinesHeader_t) +
3870 // Worst case: assume each sequence point will require its own
3871 // CV_DebugSLinesFileBlockHeader_t
3872 (lineSize * (sizeof(CV_DebugSLinesFileBlockHeader_t) + sizeof(CV_Line_t)));
3873 if (!FitsIn<ULONG32>(cbLinesSubsectionEstimate))
3874 {
3875 return NULL;
3876 }
3877
3878 LPBYTE rgbLinesSubsection = new BYTE[ULONG32(cbLinesSubsectionEstimate)];
3879 LPBYTE pbLinesSubsectionCur = rgbLinesSubsection;
3880
3881 // * (1) DWORD = CV_SIGNATURE_C13 -- the usual subsection signature DWORD
3882 *((DWORD *)pbLinesSubsectionCur) = CV_SIGNATURE_C13;
3883 pbLinesSubsectionCur += sizeof(DWORD);
3884
3885 // * (2) CV_DebugSSubsectionHeader_t
3886 CV_DebugSSubsectionHeader_t * pSubSectHeader = (CV_DebugSSubsectionHeader_t *)pbLinesSubsectionCur;
3887 memset(pSubSectHeader, 0, sizeof(*pSubSectHeader));
3888 pSubSectHeader->type = type;
3889 *ppSubSectHeader = pSubSectHeader;
3890 // pSubSectHeader->cblen to be filled in later once we know the size
3891 pbLinesSubsectionCur += sizeof(*pSubSectHeader);
3892
3893 // * (3) CV_DebugSLinesHeader_t
3894 CV_DebugSLinesHeader_t * pLinesHeader = (CV_DebugSLinesHeader_t *)pbLinesSubsectionCur;
3895 memset(pLinesHeader, 0, sizeof(*pLinesHeader));
3896 pLinesHeader->offCon = ulCodeStartOffset;
3897 pLinesHeader->segCon = m_iCodeSection;
3898 pLinesHeader->flags = 0; // 0 means line info, but not column info, is included
3899 pLinesHeader->cbCon = cbCode;
3900 *ppLinesHeader = pLinesHeader;
3901 pbLinesSubsectionCur += sizeof(*pLinesHeader);
3902 *ppbLinesSubsectionCur = pbLinesSubsectionCur;
3903 return rgbLinesSubsection;
3904}
3905
3906//---------------------------------------------------------------------------------------
3907//
3908// Helper called by NGenMethodLinesPdbWriter::WritePDBData to do the actual PDB writing of a single
3909// lines-subsection. This is called once for the hot region, and once for the cold
3910// region, of a given method that has been split. That means you get two
3911// lines-subsections for split methods.
3912//
3913// Arguments:
3914// * ulCodeStartOffset - Offset relative to the code section, or where this region
3915// of code begins
3916// * cbCode - Size in bytes of this region of code
3917// * rgMapIndexPairs - Array of indices forming the merged data from the JIT
3918// Manager's IL-to-native map and the IL PDB's IL-to-source map. It is assumed
3919// that this array has indices sorted such that the native offsets increase
3920// * cMapIndexPairs - Size in entries of above array.
3921//
3922// Assumptions:
3923// rgMapIndexPairs must be sorted in order of nativeOffset, i.e.,
3924// m_rgIlNativeMap[rgMapIndexPairs[i].m_iIlNativeMap].nativeOffset increases with i.
3925//
3926
3927HRESULT NGenMethodLinesPdbWriter::WriteDebugSLinesSubsection(
3928 ULONG32 ulCodeStartOffset,
3929 ULONG32 cbCode,
3930 MapIndexPair * rgMapIndexPairs,
3931 ULONG32 cMapIndexPairs)
3932{
3933 STANDARD_VM_CONTRACT;
3934
3935 // The lines subsection of the PDB (i.e., "DEBUG_S_LINES"), is a blob consisting of a
3936 // few structs stacked one after the other:
3937 //
3938 // * (1) DWORD = CV_SIGNATURE_C13 -- the usual subsection signature DWORD
3939 // * (2) CV_DebugSSubsectionHeader_t -- the usual subsection header, with type =
3940 // DEBUG_S_LINES
3941 // * (3) CV_DebugSLinesHeader_t -- a single header for the entire subsection. Its
3942 // purpose is to specify the native function being described, and to specify the
3943 // size of the variable-sized "blocks" that follow
3944 // * (4) CV_DebugSLinesFileBlockHeader_t -- For each block, you get one of these. A
3945 // block is defined by a set of sequence points that map to the same source
3946 // file. While iterating through the offsets, we need to define new blocks
3947 // whenever the source file changes. In C#, this typically only happens when
3948 // you advance to (or away from) an unmapped IP (0xFeeFee).
3949 // * (5) CV_Line_t (Line array entries) -- For each block, you get several line
3950 // array entries, one entry for the beginning of each sequence point.
3951
3952 HRESULT hr;
3953
3954
3955 CV_DebugSSubsectionHeader_t * pSubSectHeader = NULL;
3956 CV_DebugSLinesHeader_t * pLinesHeader = NULL;
3957 CV_DebugSLinesFileBlockHeader_t * LinesFileBlockHeader = NULL;
3958
3959 // the InitDebugLinesHeaderSection will help us taking care of
3960 // * (1) DWORD = CV_SIGNATURE_C13
3961 // * (2) CV_DebugSSubsectionHeader_t
3962 // * (3) CV_DebugSLinesHeader_t
3963 LPBYTE pbLinesSubsectionCur;
3964 LPBYTE prgbLinesSubsection = InitDebugLinesHeaderSection(
3965 DEBUG_S_LINES,
3966 ulCodeStartOffset,
3967 cbCode,
3968 cMapIndexPairs,
3969 &pSubSectHeader,
3970 &pLinesHeader,
3971 &pbLinesSubsectionCur);
3972
3973 if (pbLinesSubsectionCur == NULL)
3974 {
3975 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
3976 }
3977
3978 NewArrayHolder<BYTE> rgbLinesSubsection(prgbLinesSubsection);
3979
3980 // The loop below takes care of
3981 // * (4) CV_DebugSLinesFileBlockHeader_t
3982 // * (5) CV_Line_t (Line array entries)
3983 //
3984 BOOL fAtLeastOneBlockWritten = FALSE;
3985 CV_DebugSLinesFileBlockHeader_t * pLinesFileBlockHeader = NULL;
3986 CV_Line_t * pLineCur = NULL;
3987 CV_Line_t * pLinePrev = NULL;
3988 CV_Line_t * pLineBlockStart = NULL;
3989 BOOL fBeginNewBlock = TRUE;
3990 ULONG32 iSeqPointsPrev = (ULONG32) -1;
3991 DWORD dwNativeOffsetPrev = (DWORD) -1;
3992 DWORD ilOffsetPrev = (DWORD) -1;
3993 WCHAR wszURLPrev[MAX_LONGPATH];
3994 memset(&wszURLPrev, 0, sizeof(wszURLPrev));
3995 LPBYTE pbEnd = NULL;
3996
3997 for (ULONG32 iMapIndexPairs=0; iMapIndexPairs < cMapIndexPairs; iMapIndexPairs++)
3998 {
3999 ULONG32 iSeqPoints = rgMapIndexPairs[iMapIndexPairs].m_iSeqPoints;
4000 ULONG32 iIlNativeMap = rgMapIndexPairs[iMapIndexPairs].m_iIlNativeMap;
4001
4002 // Sometimes the JIT manager will give us duplicate IPs in the IL-to-native
4003 // offset mapping. PDB format frowns on that. Since rgMapIndexPairs is being
4004 // iterated in native offset order, it's easy to find these dupes right now, and
4005 // skip all but the first map containing a given IP offset.
4006 if (pLinePrev != NULL && m_rgIlNativeMap[iIlNativeMap].nativeOffset == pLinePrev->offset)
4007 {
4008 if (ilOffsetPrev == kUnmappedIP)
4009 {
4010 // if the previous IL offset is kUnmappedIP, then we should rewrite it.
4011 pLineCur = pLinePrev;
4012 }
4013 else if (iSeqPoints != kUnmappedIP &&
4014 m_rgilOffsets[iSeqPoints] < ilOffsetPrev)
4015 {
4016 pLineCur = pLinePrev;
4017 }
4018 else
4019 {
4020 // Found a native offset dupe, ignore the current map entry
4021 continue;
4022 }
4023 }
4024
4025 if ((iSeqPoints != kUnmappedIP) && (iSeqPoints != iSeqPointsPrev))
4026 {
4027 // This is the first iteration where we're looking at this iSeqPoints. So
4028 // check whether the document name has changed on us. If it has, that means
4029 // we need to start a new block.
4030 WCHAR wszURL[MAX_LONGPATH];
4031 ULONG32 cchURL;
4032 hr = m_rgpDocs[iSeqPoints]->GetURL(_countof(wszURL), &cchURL, wszURL);
4033 if (FAILED(hr))
4034 {
4035 // Skip function if IL PDB has data missing
4036 return S_OK;
4037 }
4038
4039 // wszURL is the best we have for a unique identifier of documents. See
4040 // whether the previous document's URL is different
4041 if (_wcsicmp(wszURL, wszURLPrev) != 0)
4042 {
4043 // New document. Update wszURLPrev, and remember that we need to start a
4044 // new file block
4045 if (wcscpy_s(wszURLPrev, _countof(wszURLPrev), wszURL) != 0)
4046 {
4047 continue;
4048 }
4049 fBeginNewBlock = TRUE;
4050 }
4051
4052 iSeqPointsPrev = iSeqPoints;
4053 }
4054 if (fBeginNewBlock)
4055 {
4056 // We've determined that we need to start a new block. So perform fixups
4057 // against the previous block (if any) first
4058 if (FinalizeLinesFileBlock(pLinesFileBlockHeader, pLineBlockStart, pLineCur))
4059 {
4060 fAtLeastOneBlockWritten = TRUE;
4061 }
4062 else if (pLinesFileBlockHeader != NULL)
4063 {
4064 // Previous block had no usable data. So rewind back to the previous
4065 // block header, and we'll start there with the next block
4066 pbLinesSubsectionCur = LPBYTE(pLinesFileBlockHeader);
4067 pLineCur = (CV_Line_t *) pbLinesSubsectionCur;
4068 }
4069
4070 // Now get the info we'll need for the next block
4071 char szURL[MAX_LONGPATH];
4072 int cbWritten = WideCharToMultiByte(
4073 CP_UTF8,
4074 0, // dwFlags
4075 wszURLPrev,
4076 -1, // i.e., input is NULL-terminated
4077 szURL, // output: UTF8 string starts here
4078 _countof(szURL), // Available space
4079 NULL, // lpDefaultChar
4080 NULL // lpUsedDefaultChar
4081 );
4082 if (cbWritten == 0)
4083 continue;
4084
4085 DocNameOffsets docNameOffsets;
4086 BOOL fExists = m_pDocNameToOffsetMap->Lookup(szURL, &docNameOffsets);
4087 if (fExists)
4088 {
4089 _ASSERTE(docNameOffsets.m_dwChksumTableOffset != (ULONG32) -1);
4090 }
4091 else
4092 {
4093 // We may get back an invalid document in the 0xFeeFee case (i.e., a
4094 // sequence point that intentionally doesn't map back to a publicly
4095 // available source code line). In that case, we'll use the bogus cksum
4096 // offset of -1 for now, and verify we're in the 0xFeeFee case later on
4097 // (see code:NGenMethodLinesPdbWriter::FinalizeLinesFileBlock).
4098 _ASSERTE(szURL[0] == '\0');
4099 _ASSERTE(docNameOffsets.m_dwChksumTableOffset == (ULONG32) -1);
4100 }
4101
4102
4103 // * (4) CV_DebugSLinesFileBlockHeader_t
4104 if (pLineCur == NULL)
4105 {
4106 // First lines file block, so begin the block header immediately after the
4107 // subsection headers
4108 pLinesFileBlockHeader = (CV_DebugSLinesFileBlockHeader_t *) pbLinesSubsectionCur;
4109 }
4110 else
4111 {
4112 // We've had blocks before this one, so add this block at our current
4113 // location in the blob
4114 pLinesFileBlockHeader = (CV_DebugSLinesFileBlockHeader_t *) pLineCur;
4115 }
4116
4117 // PDB structure sizes guarantee this is the case, though their docs are
4118 // explicit that each lines-file block header must be 4-byte aligned.
4119 _ASSERTE(IS_ALIGNED(pLinesFileBlockHeader, 4));
4120
4121 memset(pLinesFileBlockHeader, 0, sizeof(*pLinesFileBlockHeader));
4122 pLinesFileBlockHeader->offFile = docNameOffsets.m_dwChksumTableOffset;
4123 // pLinesFileBlockHeader->nLines to be filled in when block is complete
4124 // pLinesFileBlockHeader->cbBlock to be filled in when block is complete
4125
4126 pLineCur = (CV_Line_t *) (pLinesFileBlockHeader + 1);
4127 pLineBlockStart = pLineCur;
4128 fBeginNewBlock = FALSE;
4129 }
4130
4131
4132 pLineCur->offset = m_rgIlNativeMap[iIlNativeMap].nativeOffset;
4133 pLineCur->linenumStart =
4134 (iSeqPoints == kUnmappedIP) ?
4135 kUnmappedIP :
4136 m_rgnLineStarts[iSeqPoints];
4137 pLineCur->deltaLineEnd = 0;
4138 pLineCur->fStatement = 1;
4139 ilOffsetPrev = (iSeqPoints == kUnmappedIP) ? kUnmappedIP : m_rgilOffsets[iSeqPoints];
4140 pLinePrev = pLineCur;
4141 pLineCur++;
4142 } // for (ULONG32 iMapIndexPairs=0; iMapIndexPairs < cMapIndexPairs; iMapIndexPairs++)
4143
4144 if (pLineCur == NULL)
4145 {
4146 // There were no lines data for this function, so don't write anything
4147 return S_OK;
4148 }
4149
4150 // Perform fixups against the last block we wrote
4151 if (FinalizeLinesFileBlock(pLinesFileBlockHeader, pLineBlockStart, pLineCur))
4152 fAtLeastOneBlockWritten = TRUE;
4153
4154 if (!fAtLeastOneBlockWritten)
4155 {
4156 // There were no valid blocks to write for this function, so don't bother
4157 // calling PDB writing API. No problem.
4158 return S_OK;
4159 }
4160
4161 // Now that we know pSubSectHeader->cbLen, fill it in
4162 pSubSectHeader->cbLen = CV_off32_t(LPBYTE(pLineCur) - LPBYTE(pLinesHeader));
4163
4164 // Subsection is now filled out, so add it.
4165 hr = m_pWriter->ModAddSymbols(
4166 m_pMod,
4167 rgbLinesSubsection,
4168
4169 // The size we pass here is the size of the entire byte array that we pass in.
4170 int(LPBYTE(pLineCur) - rgbLinesSubsection));
4171
4172 if (FAILED(hr))
4173 return hr;
4174
4175 return S_OK;
4176}
4177
4178//---------------------------------------------------------------------------------------
4179//
4180// Helper called by NGenMethodLinesPdbWriter::WriteNativeILMapPDBData to do the actual PDB writing of a single
4181// lines-subsection. This is called once for the hot region, and once for the cold
4182// region, of a given method that has been split. That means you get two
4183// lines-subsections for split methods.
4184//
4185// Arguments:
4186// * ulCodeStartOffset - Offset relative to the code section, or where this region
4187// of code begins
4188// * cbCode - Size in bytes of this region of code
4189// * rgIlNativeMap - IL to Native map array.
4190// * rgILNativeMapAdjustSize - the number of elements we need to read in rgILNativeMap.
4191//
4192
4193HRESULT NGenMethodLinesPdbWriter::WriteDebugSILLinesSubsection(
4194 ULONG32 ulCodeStartOffset,
4195 ULONG32 cbCode,
4196 ICorDebugInfo::OffsetMapping * rgIlNativeMap,
4197 ULONG32 rgILNativeMapAdjustSize)
4198{
4199 STANDARD_VM_CONTRACT;
4200
4201 // The lines subsection of the PDB (i.e., "DEBUG_S_IL_LINES"), is a blob consisting of a
4202 // few structs stacked one after the other:
4203 //
4204 // * (1) DWORD = CV_SIGNATURE_C13 -- the usual subsection signature DWORD
4205 // * (2) CV_DebugSSubsectionHeader_t -- the usual subsection header, with type =
4206 // DEBUG_S_LINES
4207 // * (3) CV_DebugSLinesHeader_t -- a single header for the entire subsection. Its
4208 // purpose is to specify the native function being described, and to specify the
4209 // size of the variable-sized "blocks" that follow
4210 // * (4) CV_DebugSLinesFileBlockHeader_t -- For each block, you get one of these. A
4211 // block is defined by a set of sequence points that map to the same source
4212 // file. While iterating through the offsets, we need to define new blocks
4213 // whenever the source file changes. In C#, this typically only happens when
4214 // you advance to (or away from) an unmapped IP (0xFeeFee).
4215 // * (5) CV_Line_t (Line array entries) -- For each block, you get several line
4216 // array entries, one entry for the beginning of each sequence point.
4217
4218 HRESULT hr;
4219
4220 CV_DebugSSubsectionHeader_t * pSubSectHeader = NULL;
4221 CV_DebugSLinesHeader_t * pLinesHeader = NULL;
4222 CV_DebugSLinesFileBlockHeader_t * pLinesFileBlockHeader = NULL;
4223
4224 // the InitDebugLinesHeaderSection will help us taking care of
4225 // * (1) DWORD = CV_SIGNATURE_C13
4226 // * (2) CV_DebugSSubsectionHeader_t
4227 // * (3) CV_DebugSLinesHeader_t
4228 LPBYTE pbLinesSubsectionCur;
4229 LPBYTE prgbLinesSubsection = InitDebugLinesHeaderSection(
4230 DEBUG_S_IL_LINES,
4231 ulCodeStartOffset,
4232 cbCode,
4233 rgILNativeMapAdjustSize,
4234 &pSubSectHeader,
4235 &pLinesHeader,
4236 &pbLinesSubsectionCur);
4237
4238 if (prgbLinesSubsection == NULL)
4239 {
4240 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
4241 }
4242
4243 NewArrayHolder<BYTE> rgbLinesSubsection(prgbLinesSubsection);
4244
4245 // The loop below takes care of
4246 // * (4) CV_DebugSLinesFileBlockHeader_t
4247 // * (5) CV_Line_t (Line array entries)
4248 //
4249 CV_Line_t * pLineCur = NULL;
4250 CV_Line_t * pLineBlockStart = NULL;
4251 BOOL fBeginNewBlock = TRUE;
4252 LPBYTE pbEnd = NULL;
4253
4254 pLinesFileBlockHeader = (CV_DebugSLinesFileBlockHeader_t *)pbLinesSubsectionCur;
4255 // PDB structure sizes guarantee this is the case, though their docs are
4256 // explicit that each lines-file block header must be 4-byte aligned.
4257 _ASSERTE(IS_ALIGNED(pLinesFileBlockHeader, 4));
4258
4259 memset(pLinesFileBlockHeader, 0, sizeof(*pLinesFileBlockHeader));
4260 char szURL[MAX_PATH];
4261 int cbWritten = WideCharToMultiByte(
4262 CP_UTF8,
4263 0, // dwFlags
4264 UNKNOWN_SOURCE_FILE_PATH,
4265 -1, // i.e., input is NULL-terminated
4266 szURL, // output: UTF8 string starts here
4267 _countof(szURL), // Available space
4268 NULL, // lpDefaultChar
4269 NULL // lpUsedDefaultChar
4270 );
4271 _ASSERTE(cbWritten > 0);
4272 DocNameOffsets docNameOffsets;
4273 m_pDocNameToOffsetMap->Lookup(szURL, &docNameOffsets);
4274 pLinesFileBlockHeader->offFile = docNameOffsets.m_dwChksumTableOffset;
4275 // pLinesFileBlockHeader->nLines to be filled in when block is complete
4276 // pLinesFileBlockHeader->cbBlock to be filled in when block is complete
4277
4278 pLineCur = (CV_Line_t *)(pLinesFileBlockHeader + 1);
4279 pLineBlockStart = pLineCur;
4280 CV_Line_t * pLinePrev = NULL;
4281
4282 for (ULONG32 iINativeMap = 0;iINativeMap < rgILNativeMapAdjustSize; iINativeMap++)
4283 {
4284 if ((rgIlNativeMap[iINativeMap].ilOffset == NO_MAPPING) ||
4285 (rgIlNativeMap[iINativeMap].ilOffset == PROLOG) ||
4286 (rgIlNativeMap[iINativeMap].ilOffset == EPILOG))
4287 {
4288 rgIlNativeMap[iINativeMap].ilOffset = kUnmappedIP;
4289 }
4290
4291 // Sometimes the JIT manager will give us duplicate native offset in the IL-to-native
4292 // offset mapping. PDB format frowns on that. Since rgMapIndexPairs is being
4293 // iterated in native offset order, it's easy to find these dupes right now, and
4294 // skip all but the first map containing a given IP offset.
4295 if (pLinePrev != NULL &&
4296 rgIlNativeMap[iINativeMap].nativeOffset == pLinePrev->offset)
4297 {
4298 if (pLinePrev->linenumStart == kUnmappedIP)
4299 {
4300 // if the previous IL offset is kUnmappedIP, then we should rewrite it.
4301 pLineCur = pLinePrev;
4302 }
4303 else if (rgIlNativeMap[iINativeMap].ilOffset != kUnmappedIP &&
4304 rgIlNativeMap[iINativeMap].ilOffset < pLinePrev->linenumStart)
4305 {
4306 pLineCur = pLinePrev;
4307 }
4308 else
4309 {
4310 // Found a native offset dupe, ignore the current map entry
4311 continue;
4312 }
4313 }
4314
4315 pLineCur->linenumStart = rgIlNativeMap[iINativeMap].ilOffset;
4316
4317 pLineCur->offset = rgIlNativeMap[iINativeMap].nativeOffset;
4318 pLineCur->fStatement = 1;
4319 pLineCur->deltaLineEnd = 0;
4320 pLinePrev = pLineCur;
4321 pLineCur++;
4322 }
4323
4324 if (pLineCur == NULL)
4325 {
4326 // There were no lines data for this function, so don't write anything
4327 return S_OK;
4328 }
4329
4330 if (!FinalizeLinesFileBlock(pLinesFileBlockHeader, pLineBlockStart, pLineCur
4331#ifdef _DEBUG
4332 , true
4333#endif
4334 ))
4335 {
4336 return S_OK;
4337 }
4338
4339 // Now that we know pSubSectHeader->cbLen, fill it in
4340 pSubSectHeader->cbLen = CV_off32_t(LPBYTE(pLineCur) - LPBYTE(pLinesHeader));
4341
4342 // Subsection is now filled out, so add it.
4343 hr = m_pWriter->ModAddSymbols(
4344 m_pMod,
4345 rgbLinesSubsection,
4346
4347 // The size we pass here is the size of the entire byte array that we pass in.
4348 long(LPBYTE(pLineCur) - rgbLinesSubsection));
4349
4350 if (FAILED(hr))
4351 return hr;
4352
4353 return S_OK;
4354}
4355
4356//---------------------------------------------------------------------------------------
4357//
4358// Performs final fixups on the last lines-file block we completed, specifically writing
4359// in the size of the block, now that it's known. Also responsible for determining
4360// whether there is even any data to write in the first place.
4361//
4362// Arguments:
4363// * pLinesFileBlockHeader - lines-file block header to write to
4364// * pLineBlockStart - First CV_Line_t * of this block
4365// * pLineBlockAfterEnd - Last CV_Line_t * of this block plus 1
4366//
4367// Return Value:
4368// * TRUE: lines-file block was nonempty, and is now finalized
4369// * FALSE: lines-file block was empty, and caller should toss it out.
4370//
4371
4372BOOL NGenMethodLinesPdbWriter::FinalizeLinesFileBlock(
4373 CV_DebugSLinesFileBlockHeader_t * pLinesFileBlockHeader,
4374 CV_Line_t * pLineBlockStart,
4375 CV_Line_t * pLineBlockAfterEnd
4376#ifdef _DEBUG
4377 , BOOL ignorekUnmappedIPCheck
4378#endif
4379 )
4380{
4381 LIMITED_METHOD_CONTRACT;
4382
4383 if (pLinesFileBlockHeader == NULL)
4384 {
4385 // If a given function has no sequence points at all, pLinesFileBlockHeader can
4386 // be NULL. No problem
4387 return FALSE;
4388 }
4389
4390 if (pLineBlockStart == pLineBlockAfterEnd)
4391 {
4392 // If we start a lines file block and then realize that there are no entries
4393 // (i.e., no valid sequence points to map), then we end up with an empty block.
4394 // No problem, just skip the block.
4395 return FALSE;
4396 }
4397
4398 _ASSERTE(pLineBlockStart != NULL);
4399 _ASSERTE(pLineBlockAfterEnd != NULL);
4400 _ASSERTE(pLineBlockAfterEnd > pLineBlockStart);
4401
4402 if (pLinesFileBlockHeader->offFile == (ULONG32) -1)
4403 {
4404 // The file offset we set for this block is invalid. This should be due to the
4405 // 0xFeeFee case (i.e., sequence points that intentionally don't map back to a
4406 // publicly available source code line). Fix up the offset to be valid (point it
4407 // at the first file), but the offset will generally be ignored by the PDB
4408 // reader.
4409#ifdef _DEBUG
4410 {
4411 if (!ignorekUnmappedIPCheck)
4412 {
4413 for (CV_Line_t * pLineCur = pLineBlockStart; pLineCur < pLineBlockAfterEnd; pLineCur++)
4414 {
4415 _ASSERTE(pLineCur->linenumStart == kUnmappedIP);
4416 }
4417 }
4418 }
4419#endif // _DEBUG
4420 pLinesFileBlockHeader->offFile = 0;
4421 }
4422
4423 // Now that we know the size of the block, finish filling out the lines file block
4424 // header
4425 pLinesFileBlockHeader->nLines = CV_off32_t(pLineBlockAfterEnd - pLineBlockStart);
4426 pLinesFileBlockHeader->cbBlock = pLinesFileBlockHeader->nLines * sizeof(CV_Line_t);
4427
4428 return TRUE;
4429}
4430#endif // NO_NGENPDB
4431#if defined(FEATURE_PERFMAP) || !defined(NO_NGENPDB)
4432HRESULT __stdcall CreatePdb(CORINFO_ASSEMBLY_HANDLE hAssembly, BSTR pNativeImagePath, BSTR pPdbPath, BOOL pdbLines, BSTR pManagedPdbSearchPath, LPCWSTR pDiasymreaderPath)
4433{
4434 STANDARD_VM_CONTRACT;
4435
4436 Assembly *pAssembly = reinterpret_cast<Assembly *>(hAssembly);
4437 _ASSERTE(pAssembly);
4438 _ASSERTE(pNativeImagePath);
4439 _ASSERTE(pPdbPath);
4440
4441#if !defined(NO_NGENPDB)
4442 NGenPdbWriter pdbWriter(
4443 pNativeImagePath,
4444 pPdbPath,
4445 pdbLines ? kPDBLines : 0,
4446 pManagedPdbSearchPath);
4447 IfFailThrow(pdbWriter.Load(pDiasymreaderPath));
4448#elif defined(FEATURE_PERFMAP)
4449 NativeImagePerfMap perfMap(pAssembly, pPdbPath);
4450#endif
4451
4452 ModuleIterator moduleIterator = pAssembly->IterateModules();
4453 Module *pModule = NULL;
4454 BOOL fAtLeastOneNativeModuleFound = FALSE;
4455
4456 while (moduleIterator.Next())
4457 {
4458 pModule = moduleIterator.GetModule();
4459
4460 if (pModule->HasNativeOrReadyToRunImage())
4461 {
4462#if !defined(NO_NGENPDB)
4463 IfFailThrow(pdbWriter.WritePDBDataForModule(pModule));
4464#elif defined(FEATURE_PERFMAP)
4465 perfMap.LogDataForModule(pModule);
4466#endif
4467 fAtLeastOneNativeModuleFound = TRUE;
4468 }
4469 }
4470
4471 if (!fAtLeastOneNativeModuleFound)
4472 {
4473 GetSvcLogger()->Printf(
4474 W("Loaded image '%s' (for input file '%s') is not a native image.\n"),
4475 pAssembly->GetManifestFile()->GetPath().GetUnicode(),
4476 pNativeImagePath);
4477 return CORDBG_E_NO_IMAGE_AVAILABLE;
4478 }
4479
4480 GetSvcLogger()->Printf(
4481#if !defined(NO_NGENPDB)
4482 W("Successfully generated PDB for native assembly '%s'.\n"),
4483#elif defined(FEATURE_PERFMAP)
4484 W("Successfully generated perfmap for native assembly '%s'.\n"),
4485#endif
4486 pNativeImagePath);
4487
4488 return S_OK;
4489}
4490#else
4491HRESULT __stdcall CreatePdb(CORINFO_ASSEMBLY_HANDLE hAssembly, BSTR pNativeImagePath, BSTR pPdbPath, BOOL pdbLines, BSTR pManagedPdbSearchPath, LPCWSTR pDiasymreaderPath)
4492{
4493 return E_NOTIMPL;
4494}
4495#endif // defined(FEATURE_PERFMAP) || !defined(NO_NGENPDB)
4496
4497// End of PDB writing code
4498// ----------------------------------------------------------------------------
4499
4500
4501BOOL CEEPreloader::CanPrerestoreEmbedClassHandle(CORINFO_CLASS_HANDLE handle)
4502{
4503 STANDARD_VM_CONTRACT;
4504
4505 if (IsReadyToRunCompilation())
4506 return FALSE;
4507
4508 TypeHandle th(handle);
4509
4510 return m_image->CanPrerestoreEagerBindToTypeHandle(th, NULL);
4511}
4512
4513BOOL CEEPreloader::CanPrerestoreEmbedMethodHandle(CORINFO_METHOD_HANDLE handle)
4514{
4515 STANDARD_VM_CONTRACT;
4516
4517 if (IsReadyToRunCompilation())
4518 return FALSE;
4519
4520 MethodDesc *pMD = (MethodDesc*) handle;
4521
4522 return m_image->CanPrerestoreEagerBindToMethodDesc(pMD, NULL);
4523}
4524
4525ICorCompilePreloader * CEECompileInfo::PreloadModule(CORINFO_MODULE_HANDLE module,
4526 ICorCompileDataStore *pData,
4527 CorProfileData *profileData)
4528{
4529 STANDARD_VM_CONTRACT;
4530
4531 NewHolder<CEEPreloader> pPreloader(new CEEPreloader((Module *) module, pData));
4532
4533 COOPERATIVE_TRANSITION_BEGIN();
4534
4535 if (PartialNGenStressPercentage() == 0)
4536 {
4537 pPreloader->Preload(profileData);
4538 }
4539
4540 COOPERATIVE_TRANSITION_END();
4541
4542 return pPreloader.Extract();
4543}
4544
4545void CEECompileInfo::SetAssemblyHardBindList(
4546 __in_ecount( cHardBindList )
4547 LPWSTR *pHardBindList,
4548 DWORD cHardBindList)
4549{
4550 STANDARD_VM_CONTRACT;
4551
4552}
4553
4554HRESULT CEECompileInfo::SetVerboseLevel(
4555 IN VerboseLevel level)
4556{
4557 LIMITED_METHOD_CONTRACT;
4558 HRESULT hr = S_OK;
4559 g_CorCompileVerboseLevel = level;
4560 return hr;
4561}
4562
4563//
4564// Preloader:
4565//
4566CEEPreloader::CEEPreloader(Module *pModule,
4567 ICorCompileDataStore *pData)
4568 : m_pData(pData)
4569{
4570 m_image = new DataImage(pModule, this);
4571
4572 CONSISTENCY_CHECK(pModule == GetAppDomain()->ToCompilationDomain()->GetTargetModule());
4573
4574 GetAppDomain()->ToCompilationDomain()->SetTargetImage(m_image, this);
4575
4576 m_methodCompileLimit = pModule->GetMDImport()->GetCountWithTokenKind(mdtMethodDef) * 10;
4577}
4578
4579CEEPreloader::~CEEPreloader()
4580{
4581 WRAPPER_NO_CONTRACT;
4582 delete m_image;
4583}
4584
4585void CEEPreloader::Preload(CorProfileData * profileData)
4586{
4587 STANDARD_VM_CONTRACT;
4588
4589 bool doNothingNgen = false;
4590#ifdef _DEBUG
4591 static ConfigDWORD fDoNothingNGen;
4592 doNothingNgen = !!fDoNothingNGen.val(CLRConfig::INTERNAL_ZapDoNothing);
4593#endif
4594
4595 if (!doNothingNgen)
4596 {
4597 m_image->GetModule()->SetProfileData(profileData);
4598 m_image->GetModule()->ExpandAll(m_image);
4599 }
4600
4601 // Triage all items created by initial expansion.
4602 // We will try to accept all items created by initial expansion.
4603 TriageForZap(TRUE);
4604}
4605
4606//
4607// ICorCompilerPreloader
4608//
4609
4610DWORD CEEPreloader::MapMethodEntryPoint(CORINFO_METHOD_HANDLE handle)
4611{
4612 STANDARD_VM_CONTRACT;
4613
4614 MethodDesc *pMD = GetMethod(handle);
4615 Precode * pPrecode = pMD->GetSavedPrecode(m_image);
4616
4617 return m_image->GetRVA(pPrecode);
4618}
4619
4620DWORD CEEPreloader::MapClassHandle(CORINFO_CLASS_HANDLE handle)
4621{
4622 STANDARD_VM_CONTRACT;
4623
4624 TypeHandle th = TypeHandle::FromPtr(handle);
4625 if (th.IsTypeDesc())
4626 return m_image->GetRVA(th.AsTypeDesc()) | 2;
4627 else
4628 return m_image->GetRVA(th.AsMethodTable());
4629}
4630
4631DWORD CEEPreloader::MapMethodHandle(CORINFO_METHOD_HANDLE handle)
4632{
4633 STANDARD_VM_CONTRACT;
4634
4635 return m_image->GetRVA(handle);
4636}
4637
4638DWORD CEEPreloader::MapFieldHandle(CORINFO_FIELD_HANDLE handle)
4639{
4640 STANDARD_VM_CONTRACT;
4641
4642 return m_image->GetRVA(handle);
4643}
4644
4645DWORD CEEPreloader::MapAddressOfPInvokeFixup(CORINFO_METHOD_HANDLE handle)
4646{
4647 STANDARD_VM_CONTRACT;
4648
4649 MethodDesc *pMD = GetMethod(handle);
4650
4651 _ASSERTE(pMD->IsNDirect());
4652 NDirectWriteableData * pMDWriteableData = ((NDirectMethodDesc *)pMD)->GetWriteableData();
4653
4654 return m_image->GetRVA(pMDWriteableData) + offsetof(NDirectWriteableData, m_pNDirectTarget);
4655}
4656
4657DWORD CEEPreloader::MapGenericHandle(CORINFO_GENERIC_HANDLE handle)
4658{
4659 STANDARD_VM_CONTRACT;
4660
4661 return m_image->GetRVA(handle);
4662}
4663
4664DWORD CEEPreloader::MapModuleIDHandle(CORINFO_MODULE_HANDLE handle)
4665{
4666 STANDARD_VM_CONTRACT;
4667
4668 return m_image->GetRVA(handle) + (DWORD)Module::GetOffsetOfModuleID();
4669}
4670
4671CORINFO_METHOD_HANDLE CEEPreloader::NextUncompiledMethod()
4672{
4673 STANDARD_VM_CONTRACT;
4674
4675 // If we have run out of methods to compile, ensure that we have code for all methods
4676 // that we are about to save.
4677 if (m_uncompiledMethods.GetCount() == 0)
4678 {
4679 // The subsequent populations are done in non-expansive way (won't load new types)
4680 m_image->GetModule()->PrepopulateDictionaries(m_image, TRUE);
4681
4682 // Make sure that we have generated code for all instantiations that we are going to save
4683 // The new items that we encounter here were most likely side effects of verification or failed inlining,
4684 // so do not try to save them eagerly.
4685 while (TriageForZap(FALSE)) {
4686 // Loop as long as new types are added
4687 }
4688 }
4689
4690 // Take next uncompiled method
4691 COUNT_T count = m_uncompiledMethods.GetCount();
4692 if (count == 0)
4693 return NULL;
4694
4695 MethodDesc * pMD = m_uncompiledMethods[count - 1];
4696 m_uncompiledMethods.SetCount(count - 1);
4697
4698#ifdef _DEBUG
4699 if (LoggingOn(LF_ZAP, LL_INFO10000))
4700 {
4701 StackSString methodString;
4702 TypeString::AppendMethodDebug(methodString, pMD);
4703
4704 LOG((LF_ZAP, LL_INFO10000, "CEEPreloader::NextUncompiledMethod: %S\n", methodString.GetUnicode()));
4705 }
4706#endif // _DEBUG
4707
4708 return (CORINFO_METHOD_HANDLE) pMD;
4709}
4710
4711void CEEPreloader::AddMethodToTransitiveClosureOfInstantiations(CORINFO_METHOD_HANDLE handle)
4712{
4713 STANDARD_VM_CONTRACT;
4714
4715 TriageMethodForZap(GetMethod(handle), TRUE);
4716}
4717
4718BOOL CEEPreloader::IsMethodInTransitiveClosureOfInstantiations(CORINFO_METHOD_HANDLE handle)
4719{
4720 STANDARD_VM_CONTRACT;
4721
4722 MethodDesc *pMD = GetMethod(handle);
4723
4724 return (m_acceptedMethods.Lookup(pMD) != NULL) && (m_rejectedMethods.Lookup(pMD) == NULL);
4725}
4726
4727BOOL CEEPreloader::IsTypeInTransitiveClosureOfInstantiations(CORINFO_CLASS_HANDLE handle)
4728{
4729 STANDARD_VM_CONTRACT;
4730
4731 TypeHandle th = (TypeHandle) handle;
4732
4733 return (m_acceptedTypes.Lookup(th) != NULL) && (m_rejectedTypes.Lookup(th) == NULL);
4734}
4735
4736void CEEPreloader::MethodReferencedByCompiledCode(CORINFO_METHOD_HANDLE handle)
4737{
4738 STANDARD_VM_CONTRACT;
4739
4740#ifndef FEATURE_FULL_NGEN // Unreferenced methods
4741 //
4742 // Keep track of methods that are actually referenced by the code. We use this information
4743 // to avoid generating code for unreferenced methods not visible outside the assembly.
4744 // These methods are very unlikely to be ever used at runtime because of they only ever be
4745 // called via private reflection.
4746 //
4747 MethodDesc *pMD = GetMethod(handle);
4748
4749 const CompileMethodEntry * pEntry = m_compileMethodsHash.LookupPtr(pMD);
4750 if (pEntry != NULL)
4751 {
4752 if (pEntry->fReferenced)
4753 return;
4754 const_cast<CompileMethodEntry *>(pEntry)->fReferenced = true;
4755
4756 if (pEntry->fScheduled)
4757 return;
4758 AppendUncompiledMethod(pMD);
4759 }
4760 else
4761 {
4762 CompileMethodEntry entry;
4763 entry.pMD = pMD;
4764 entry.fReferenced = true;
4765 entry.fScheduled = false;
4766 m_compileMethodsHash.Add(entry);
4767 }
4768
4769 if (pMD->IsWrapperStub())
4770 MethodReferencedByCompiledCode((CORINFO_METHOD_HANDLE)pMD->GetWrappedMethodDesc());
4771#endif // FEATURE_FULL_NGEN
4772}
4773
4774BOOL CEEPreloader::IsUncompiledMethod(CORINFO_METHOD_HANDLE handle)
4775{
4776 STANDARD_VM_CONTRACT;
4777
4778 MethodDesc *pMD = GetMethod(handle);
4779
4780#ifndef FEATURE_FULL_NGEN // Unreferenced methods
4781 const CompileMethodEntry * pEntry = m_compileMethodsHash.LookupPtr(pMD);
4782 return (pEntry != NULL) && (pEntry->fScheduled || !pEntry->fReferenced);
4783#else
4784 return m_compileMethodsHash.LookupPtr(pMD) != NULL;
4785#endif
4786}
4787
4788static bool IsTypeAccessibleOutsideItsAssembly(TypeHandle th)
4789{
4790 STANDARD_VM_CONTRACT;
4791
4792 if (th.IsTypeDesc())
4793 {
4794 if (th.AsTypeDesc()->HasTypeParam())
4795 return IsTypeAccessibleOutsideItsAssembly(th.AsTypeDesc()->GetTypeParam());
4796
4797 return true;
4798 }
4799
4800 MethodTable * pMT = th.AsMethodTable();
4801
4802 if (pMT == g_pCanonMethodTableClass)
4803 return true;
4804
4805 switch (pMT->GetClass()->GetProtection())
4806 {
4807 case tdPublic:
4808 break;
4809 case tdNestedPublic:
4810 case tdNestedFamily:
4811 case tdNestedFamORAssem:
4812 {
4813 MethodTable * pMTEnclosing = pMT->LoadEnclosingMethodTable();
4814 if (pMTEnclosing == NULL)
4815 return false;
4816 if (!IsTypeAccessibleOutsideItsAssembly(pMTEnclosing))
4817 return false;
4818 }
4819 break;
4820
4821 default:
4822 return false;
4823 }
4824
4825 if (pMT->HasInstantiation())
4826 {
4827 Instantiation instantiation = pMT->GetInstantiation();
4828 for (DWORD i = 0; i < instantiation.GetNumArgs(); i++)
4829 {
4830 if (!IsTypeAccessibleOutsideItsAssembly(instantiation[i]))
4831 return false;
4832 }
4833 }
4834
4835 return true;
4836}
4837
4838static bool IsMethodAccessibleOutsideItsAssembly(MethodDesc * pMD)
4839{
4840 STANDARD_VM_CONTRACT;
4841
4842 // Note that this ignores friend access.
4843
4844 switch (pMD->GetAttrs() & mdMemberAccessMask)
4845 {
4846 case mdFamily:
4847 case mdFamORAssem:
4848 case mdPublic:
4849 break;
4850
4851 default:
4852 return false;
4853 }
4854
4855 if (!IsTypeAccessibleOutsideItsAssembly(pMD->GetMethodTable()))
4856 return false;
4857
4858 if (pMD->HasMethodInstantiation())
4859 {
4860 Instantiation instantiation = pMD->GetMethodInstantiation();
4861 for (DWORD i = 0; i < instantiation.GetNumArgs(); i++)
4862 {
4863 if (!IsTypeAccessibleOutsideItsAssembly(instantiation[i]))
4864 return false;
4865 }
4866 }
4867
4868 return true;
4869}
4870
4871static bool IsMethodCallableOutsideItsAssembly(MethodDesc * pMD)
4872{
4873 STANDARD_VM_CONTRACT;
4874
4875 // Virtual methods can be called via interfaces, etc. We would need to do
4876 // more analysis to trim them. For now, assume that they can be referenced outside this assembly.
4877 if (pMD->IsVirtual())
4878 return true;
4879
4880 // Class constructors are often used with reflection. Always generate code for them.
4881 if (pMD->IsClassConstructorOrCtor())
4882 return true;
4883
4884 if (IsMethodAccessibleOutsideItsAssembly(pMD))
4885 return true;
4886
4887 return false;
4888}
4889
4890BOOL IsGenericTooDeeplyNested(TypeHandle t);
4891void CEEPreloader::AddToUncompiledMethods(MethodDesc *pMD, BOOL fForStubs)
4892{
4893 STANDARD_VM_CONTRACT;
4894
4895 // TriageTypeForZap() and TriageMethodForZap() should ensure this.
4896 _ASSERTE(m_image->GetModule() == pMD->GetLoaderModule());
4897
4898 if (!fForStubs)
4899 {
4900 if (!pMD->IsIL())
4901 return;
4902
4903 if (!pMD->MayHaveNativeCode() && !pMD->IsWrapperStub())
4904 return;
4905 }
4906
4907 // If it's already been compiled, don't add it to the set of uncompiled methods
4908 if (m_image->GetCodeAddress(pMD) != NULL)
4909 return;
4910
4911 // If it's already in the queue to be compiled don't add it again
4912 const CompileMethodEntry * pEntry = m_compileMethodsHash.LookupPtr(pMD);
4913
4914#ifndef FEATURE_FULL_NGEN // Unreferenced methods
4915 if (pEntry != NULL)
4916 {
4917 if (pEntry->fScheduled)
4918 return;
4919
4920 if (!pEntry->fReferenced)
4921 return;
4922
4923 const_cast<CompileMethodEntry *>(pEntry)->fScheduled = true;
4924 }
4925 else
4926 {
4927 // The unreferenced methods optimization works for generic methods and methods on generic types only.
4928 // Non-generic methods take different path.
4929 //
4930 // It unclear whether it is worth it to enable it for non-generic methods too. The benefit
4931 // for non-generic methods is small, and the non-generic methods are more likely to be called
4932 // via private reflection.
4933
4934 bool fSchedule = fForStubs || IsMethodCallableOutsideItsAssembly(pMD);
4935
4936 CompileMethodEntry entry;
4937 entry.pMD = pMD;
4938 entry.fScheduled = fSchedule;
4939 entry.fReferenced = false;
4940 m_compileMethodsHash.Add(entry);
4941
4942 if (!fSchedule)
4943 return;
4944 }
4945#else // // FEATURE_FULL_NGEN
4946 // Schedule the method for compilation
4947 if (pEntry != NULL)
4948 return;
4949 CompileMethodEntry entry;
4950 entry.pMD = pMD;
4951 m_compileMethodsHash.Add(entry);
4952#endif // FEATURE_FULL_NGEN
4953
4954 if (pMD->HasMethodInstantiation())
4955 {
4956 Instantiation instantiation = pMD->GetMethodInstantiation();
4957 for (DWORD i = 0; i < instantiation.GetNumArgs(); i++)
4958 {
4959 if (IsGenericTooDeeplyNested(instantiation[i]))
4960 return;
4961 }
4962 }
4963
4964 // Add it to the set of uncompiled methods
4965 AppendUncompiledMethod(pMD);
4966}
4967
4968//
4969// Used to validate instantiations produced by the production rules before we actually try to instantiate them.
4970//
4971static BOOL CanSatisfyConstraints(Instantiation typicalInst, Instantiation candidateInst)
4972{
4973 STANDARD_VM_CONTRACT;
4974
4975 // The dependency must be of the form C<T> --> D<T>
4976 _ASSERTE(typicalInst.GetNumArgs() == candidateInst.GetNumArgs());
4977 if (typicalInst.GetNumArgs() != candidateInst.GetNumArgs())
4978 return FALSE;
4979
4980 SigTypeContext typeContext(candidateInst, Instantiation());
4981
4982 for (DWORD i = 0; i < candidateInst.GetNumArgs(); i++)
4983 {
4984 TypeHandle thArg = candidateInst[i];
4985
4986 // If this is "__Canon" and we are code sharing then we can't rule out that some
4987 // compatible instantiation may meet the constraints
4988 if (thArg == TypeHandle(g_pCanonMethodTableClass))
4989 continue;
4990
4991 // Otherwise we approximate, and just assume that we have "parametric" constraints
4992 // of the form "T : IComparable<T>" rather than "odd" constraints such as "T : IComparable<string>".
4993 // That is, we assume checking the constraint at the canonical type is sufficient
4994 // to tell us if the constraint holds for all compatible types.
4995 //
4996 // For example of where this does not hold, consider if
4997 // class C<T>
4998 // class D<T> where T : IComparable<T>
4999 // struct Struct<T> : IComparable<string>
5000 // Assume we generate C<Struct<object>>. Now the constraint
5001 // Struct<object> : IComparable<object>
5002 // does not hold, so we do not generate the instantiation, even though strictly speaking
5003 // the compatible instantiation C<Struct<string>> will satisfy the constraint
5004 // Struct<string> : IComparable<string>
5005
5006 TypeVarTypeDesc* tyvar = typicalInst[i].AsGenericVariable();
5007
5008 tyvar->LoadConstraints();
5009
5010 if (!tyvar->SatisfiesConstraints(&typeContext,thArg)) {
5011#ifdef _DEBUG
5012 /*
5013 // In case we want to know which illegal instantiations we ngen'ed
5014 StackSString candidateInstName;
5015 StackScratchBuffer buffer;
5016 thArg.GetName(candidateInstName);
5017 char output[1024];
5018 _snprintf_s(output, _countof(output), _TRUNCATE, "Generics TypeDependencyAttribute processing: Couldn't satisfy a constraint. Class with Attribute: %s Bad candidate instantiated type: %s\r\n", pMT->GetDebugClassName(), candidateInstName.GetANSI(buffer));
5019 OutputDebugStringA(output);
5020 */
5021#endif
5022 return FALSE;
5023 }
5024 }
5025
5026 return TRUE;
5027}
5028
5029
5030//
5031// This method has duplicated logic from bcl\system\collections\generic\comparer.cs
5032//
5033static void SpecializeComparer(SString& ss, Instantiation& inst)
5034{
5035 STANDARD_VM_CONTRACT;
5036
5037 if (inst.GetNumArgs() != 1) {
5038 _ASSERTE(!"Improper use of a TypeDependencyAttribute for Comparer");
5039 return;
5040 }
5041
5042 TypeHandle elemTypeHnd = inst[0];
5043
5044 //
5045 // Override the default ObjectComparer for special cases
5046 //
5047 if (elemTypeHnd.CanCastTo(
5048 TypeHandle(MscorlibBinder::GetClass(CLASS__ICOMPARABLEGENERIC)).Instantiate(Instantiation(&elemTypeHnd, 1))))
5049 {
5050 ss.Set(W("System.Collections.Generic.GenericComparer`1"));
5051 return;
5052 }
5053
5054 if (Nullable::IsNullableType(elemTypeHnd))
5055 {
5056 Instantiation nullableInst = elemTypeHnd.AsMethodTable()->GetInstantiation();
5057 if (nullableInst[0].CanCastTo(
5058 TypeHandle(MscorlibBinder::GetClass(CLASS__ICOMPARABLEGENERIC)).Instantiate(nullableInst)))
5059 {
5060 ss.Set(W("System.Collections.Generic.NullableComparer`1"));
5061 inst = nullableInst;
5062 return;
5063 }
5064 }
5065
5066 if (elemTypeHnd.IsEnum())
5067 {
5068 CorElementType et = elemTypeHnd.GetVerifierCorElementType();
5069 if (et == ELEMENT_TYPE_I1 ||
5070 et == ELEMENT_TYPE_I2 ||
5071 et == ELEMENT_TYPE_I4)
5072 {
5073 ss.Set(W("System.Collections.Generic.Int32EnumComparer`1"));
5074 return;
5075 }
5076 if (et == ELEMENT_TYPE_U1 ||
5077 et == ELEMENT_TYPE_U2 ||
5078 et == ELEMENT_TYPE_U4)
5079 {
5080 ss.Set(W("System.Collections.Generic.UInt32EnumComparer`1"));
5081 return;
5082 }
5083 if (et == ELEMENT_TYPE_I8)
5084 {
5085 ss.Set(W("System.Collections.Generic.Int64EnumComparer`1"));
5086 return;
5087 }
5088 if (et == ELEMENT_TYPE_U8)
5089 {
5090 ss.Set(W("System.Collections.Generic.UInt64EnumComparer`1"));
5091 return;
5092 }
5093 }
5094}
5095
5096//
5097// This method has duplicated logic from bcl\system\collections\generic\equalitycomparer.cs
5098// and matching logic in jitinterface.cpp
5099//
5100static void SpecializeEqualityComparer(SString& ss, Instantiation& inst)
5101{
5102 STANDARD_VM_CONTRACT;
5103
5104 if (inst.GetNumArgs() != 1) {
5105 _ASSERTE(!"Improper use of a TypeDependencyAttribute for EqualityComparer");
5106 return;
5107 }
5108
5109 TypeHandle elemTypeHnd = inst[0];
5110
5111 //
5112 // Override the default ObjectEqualityComparer for special cases
5113 //
5114 if (elemTypeHnd.CanCastTo(
5115 TypeHandle(MscorlibBinder::GetClass(CLASS__IEQUATABLEGENERIC)).Instantiate(Instantiation(&elemTypeHnd, 1))))
5116 {
5117 ss.Set(W("System.Collections.Generic.GenericEqualityComparer`1"));
5118 return;
5119 }
5120
5121 if (Nullable::IsNullableType(elemTypeHnd))
5122 {
5123 Instantiation nullableInst = elemTypeHnd.AsMethodTable()->GetInstantiation();
5124 if (nullableInst[0].CanCastTo(
5125 TypeHandle(MscorlibBinder::GetClass(CLASS__IEQUATABLEGENERIC)).Instantiate(nullableInst)))
5126 {
5127 ss.Set(W("System.Collections.Generic.NullableEqualityComparer`1"));
5128 inst = nullableInst;
5129 return;
5130 }
5131 }
5132
5133 if (elemTypeHnd.IsEnum())
5134 {
5135 // Note: We have different comparers for Short and SByte because for those types we need to make sure we call GetHashCode on the actual underlying type as the
5136 // implementation of GetHashCode is more complex than for the other types.
5137 CorElementType et = elemTypeHnd.GetVerifierCorElementType();
5138 if (et == ELEMENT_TYPE_I4 ||
5139 et == ELEMENT_TYPE_U4 ||
5140 et == ELEMENT_TYPE_U2 ||
5141 et == ELEMENT_TYPE_I2 ||
5142 et == ELEMENT_TYPE_U1 ||
5143 et == ELEMENT_TYPE_I1)
5144 {
5145 ss.Set(W("System.Collections.Generic.EnumEqualityComparer`1"));
5146 return;
5147 }
5148 else if (et == ELEMENT_TYPE_I8 ||
5149 et == ELEMENT_TYPE_U8)
5150 {
5151 ss.Set(W("System.Collections.Generic.LongEnumEqualityComparer`1"));
5152 return;
5153 }
5154 }
5155}
5156
5157#ifdef FEATURE_COMINTEROP
5158// Instantiation of WinRT types defined in non-WinRT module. This check is required to generate marshaling stubs for
5159// instantiations of shadow WinRT types like EventHandler<ITracingStatusChangedEventArgs> in mscorlib.
5160static BOOL IsInstantationOfShadowWinRTType(MethodTable * pMT)
5161{
5162 STANDARD_VM_CONTRACT;
5163
5164 Instantiation inst = pMT->GetInstantiation();
5165 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
5166 {
5167 TypeHandle th = inst[i];
5168 if (th.IsProjectedFromWinRT() && !th.GetModule()->IsWindowsRuntimeModule())
5169 return TRUE;
5170 }
5171 return FALSE;
5172}
5173#endif
5174
5175void CEEPreloader::ApplyTypeDependencyProductionsForType(TypeHandle t)
5176{
5177 STANDARD_VM_CONTRACT;
5178
5179 // Only actual types
5180 if (t.IsTypeDesc())
5181 return;
5182
5183 MethodTable * pMT = t.AsMethodTable();
5184
5185 if (!pMT->HasInstantiation() || pMT->ContainsGenericVariables())
5186 return;
5187
5188#ifdef FEATURE_COMINTEROP
5189 // At run-time, generic redirected interfaces and delegates need matching instantiations
5190 // of other types/methods in order to be marshaled across the interop boundary.
5191 if (m_image->GetModule()->IsWindowsRuntimeModule() || IsInstantationOfShadowWinRTType(pMT))
5192 {
5193 // We only apply WinRT dependencies when compiling .winmd assemblies since redirected
5194 // types are heavily used in non-WinRT code as well and would bloat native images.
5195 if (pMT->IsLegalNonArrayWinRTType())
5196 {
5197 TypeHandle thWinRT;
5198 WinMDAdapter::RedirectedTypeIndex index;
5199 if (WinRTInterfaceRedirector::ResolveRedirectedInterface(pMT, &index))
5200 {
5201 // redirected interface needs the mscorlib-local definition of the corresponding WinRT type
5202 MethodTable *pWinRTMT = WinRTInterfaceRedirector::GetWinRTTypeForRedirectedInterfaceIndex(index);
5203 thWinRT = TypeHandle(pWinRTMT);
5204
5205 // and matching stub methods
5206 WORD wNumSlots = pWinRTMT->GetNumVirtuals();
5207 for (WORD i = 0; i < wNumSlots; i++)
5208 {
5209 MethodDesc *pAdapterMD = WinRTInterfaceRedirector::GetStubMethodForRedirectedInterface(
5210 index,
5211 i,
5212 TypeHandle::Interop_NativeToManaged,
5213 FALSE,
5214 pMT->GetInstantiation());
5215
5216 TriageMethodForZap(pAdapterMD, TRUE);
5217 }
5218 }
5219 if (WinRTDelegateRedirector::ResolveRedirectedDelegate(pMT, &index))
5220 {
5221 // redirected delegate needs the mscorlib-local definition of the corresponding WinRT type
5222 thWinRT = TypeHandle(WinRTDelegateRedirector::GetWinRTTypeForRedirectedDelegateIndex(index));
5223 }
5224
5225 if (!thWinRT.IsNull())
5226 {
5227 thWinRT = thWinRT.Instantiate(pMT->GetInstantiation());
5228 TriageTypeForZap(thWinRT, TRUE);
5229 }
5230 }
5231 }
5232#endif // FEATURE_COMINTEROP
5233
5234 pMT = pMT->GetCanonicalMethodTable();
5235
5236 // The TypeDependencyAttribute attribute is currently only allowed on mscorlib types
5237 // Don't even look for the attribute on types in other assemblies.
5238 if(!pMT->GetModule()->IsSystem()) {
5239 return;
5240 }
5241
5242 // Part 1. - check for an NGEN production rule specified by a use of CompilerServices.TypeDependencyAttribute
5243 // e.g. C<T> --> D<T>
5244 //
5245 // For example, if C<int> is generated then we produce D<int>.
5246 //
5247 // Normally NGEN can detect such productions through the process of compilation, but there are some
5248 // legitimate uses of reflection to generate generic instantiations which NGEN cannot detect.
5249 // In particular typically D<T> will have more constraints than C<T>, e.g.
5250 // class D<T> where T : IComparable<T>
5251 // Uses of dynamic constraints are an example - consider making a Comparer<T>, where we can have a
5252 // FastComparer<T> where T : IComparable<T>, and the "slow" version checks for the non-generic
5253 // IComparer interface.
5254 // Also, T[] : IList<T>, IReadOnlyList<T>, and both of those interfaces should have a type dependency on SZArrayHelper's generic methods.
5255 //
5256 IMDInternalImport *pImport = pMT->GetMDImport();
5257 HRESULT hr;
5258
5259 _ASSERTE(pImport);
5260 //walk all of the TypeDependencyAttributes
5261 MDEnumHolder hEnum(pImport);
5262 hr = pImport->EnumCustomAttributeByNameInit(pMT->GetCl(),
5263 g_CompilerServicesTypeDependencyAttribute, &hEnum);
5264 if (SUCCEEDED(hr))
5265 {
5266 mdCustomAttribute tkAttribute;
5267 const BYTE *pbAttr;
5268 ULONG cbAttr;
5269
5270 while (pImport->EnumNext(&hEnum, &tkAttribute))
5271 {
5272 //get attribute and validate format
5273 if (FAILED(pImport->GetCustomAttributeAsBlob(
5274 tkAttribute,
5275 reinterpret_cast<const void **>(&pbAttr),
5276 &cbAttr)))
5277 {
5278 continue;
5279 }
5280
5281 CustomAttributeParser cap(pbAttr, cbAttr);
5282 if (FAILED(cap.SkipProlog()))
5283 continue;
5284
5285 LPCUTF8 szString;
5286 ULONG cbString;
5287 if (FAILED(cap.GetNonNullString(&szString, &cbString)))
5288 continue;
5289
5290 StackSString ss(SString::Utf8, szString, cbString);
5291 Instantiation inst = pMT->GetInstantiation();
5292
5293#ifndef FEATURE_FULL_NGEN
5294 // Do not expand non-canonical instantiations. They are not that expensive to create at runtime
5295 // using code:ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation if necessary.
5296 if (!ClassLoader::IsCanonicalGenericInstantiation(inst))
5297 continue;
5298#endif
5299
5300 if (ss.Equals(W("System.Collections.Generic.ObjectComparer`1")))
5301 {
5302 SpecializeComparer(ss, inst);
5303 }
5304 else
5305 if (ss.Equals(W("System.Collections.Generic.ObjectEqualityComparer`1")))
5306 {
5307 SpecializeEqualityComparer(ss, inst);
5308 }
5309
5310 // Try to load the class using its name as a fully qualified name. If that fails,
5311 // then we try to load it in the assembly of the current class.
5312 TypeHandle typicalDepTH = TypeName::GetTypeUsingCASearchRules(ss.GetUnicode(), pMT->GetAssembly());
5313
5314 _ASSERTE(!typicalDepTH.IsNull());
5315 // This attribute is currently only allowed to refer to mscorlib types
5316 _ASSERTE(typicalDepTH.GetModule()->IsSystem());
5317 if (!typicalDepTH.GetModule()->IsSystem())
5318 continue;
5319
5320 // For IList<T>, ICollection<T>, IEnumerable<T>, IReadOnlyCollection<T> & IReadOnlyList<T>, include SZArrayHelper's
5321 // generic methods (or at least the relevant ones) in the ngen image in
5322 // case someone casts a T[] to an IList<T> (or ICollection<T> or IEnumerable<T>, etc).
5323 if (MscorlibBinder::IsClass(typicalDepTH.AsMethodTable(), CLASS__SZARRAYHELPER))
5324 {
5325#ifdef FEATURE_FULL_NGEN
5326 if (pMT->GetNumGenericArgs() != 1 || !pMT->IsInterface()) {
5327 _ASSERTE(!"Improper use of a TypeDependencyAttribute for SZArrayHelper");
5328 continue;
5329 }
5330 TypeHandle elemTypeHnd = pMT->GetInstantiation()[0];
5331 if (elemTypeHnd.IsValueType())
5332 ApplyTypeDependencyForSZArrayHelper(pMT, elemTypeHnd);
5333#endif
5334 continue;
5335 }
5336
5337 _ASSERTE(typicalDepTH.IsTypicalTypeDefinition());
5338 if (!typicalDepTH.IsTypicalTypeDefinition())
5339 continue;
5340
5341 // It certainly can't be immediately recursive...
5342 _ASSERTE(!typicalDepTH.GetMethodTable()->HasSameTypeDefAs(pMT));
5343
5344 // We want to rule out some cases where we know for sure that the generated type
5345 // won't satisfy its constraints. However, some generated types may represent
5346 // canonicals in sets of shared instantaitions,
5347
5348 if (CanSatisfyConstraints(typicalDepTH.GetInstantiation(), inst))
5349 {
5350 TypeHandle instDepTH =
5351 ClassLoader::LoadGenericInstantiationThrowing(typicalDepTH.GetModule(), typicalDepTH.GetCl(), inst);
5352
5353 _ASSERTE(!instDepTH.ContainsGenericVariables());
5354 _ASSERTE(instDepTH.GetNumGenericArgs() == typicalDepTH.GetNumGenericArgs());
5355 _ASSERTE(instDepTH.GetMethodTable()->HasSameTypeDefAs(typicalDepTH.GetMethodTable()));
5356
5357 // OK, add the generated type to the dependency set
5358 TriageTypeForZap(instDepTH, TRUE);
5359 }
5360 }
5361 }
5362} // CEEPreloader::ApplyTypeDependencyProductionsForType
5363
5364
5365// Given IEnumerable<Foo>, we want to add System.SZArrayHelper.GetEnumerator<Foo>
5366// to the ngen image. This way we can cast a T[] to an IList<T> and
5367// use methods on it (from SZArrayHelper) without pulling in the JIT.
5368// Do the same for ICollection<T>/IReadOnlyCollection<T> and
5369// IList<T>/IReadOnlyList<T>, but only add the relevant methods
5370// from those interfaces.
5371void CEEPreloader::ApplyTypeDependencyForSZArrayHelper(MethodTable * pInterfaceMT, TypeHandle elemTypeHnd)
5372{
5373 STANDARD_VM_CONTRACT;
5374
5375 _ASSERTE(elemTypeHnd.AsMethodTable()->IsValueType());
5376
5377 // We expect this to only be called for IList<T>/IReadOnlyList<T>, ICollection<T>/IReadOnlyCollection<T>, IEnumerable<T>.
5378 _ASSERTE(pInterfaceMT->IsInterface());
5379 _ASSERTE(pInterfaceMT->GetNumGenericArgs() == 1);
5380
5381 // This is the list of methods that don't throw exceptions on SZArrayHelper.
5382 static const BinderMethodID SZArrayHelperMethodIDs[] = {
5383 // Read-only methods that are present on both regular and read-only interfaces.
5384 METHOD__SZARRAYHELPER__GETENUMERATOR,
5385 METHOD__SZARRAYHELPER__GET_COUNT,
5386 METHOD__SZARRAYHELPER__GET_ITEM,
5387 // The rest of the methods is present on regular interfaces only.
5388 METHOD__SZARRAYHELPER__SET_ITEM,
5389 METHOD__SZARRAYHELPER__COPYTO,
5390 METHOD__SZARRAYHELPER__INDEXOF,
5391 METHOD__SZARRAYHELPER__CONTAINS };
5392
5393 static const int cReadOnlyMethods = 3;
5394 static const int cAllMethods = 7;
5395
5396 static const BinderMethodID LastMethodOnGenericArrayInterfaces[] = {
5397 METHOD__SZARRAYHELPER__GETENUMERATOR, // Last method of IEnumerable<T>
5398 METHOD__SZARRAYHELPER__REMOVE, // Last method of ICollection<T>.
5399 METHOD__SZARRAYHELPER__REMOVEAT, // Last method of IList<T>
5400 };
5401
5402 // Assuming the binder ID's are properly laid out in mscorlib.h
5403#if _DEBUG
5404 for(unsigned int i=0; i < NumItems(LastMethodOnGenericArrayInterfaces) - 1; i++) {
5405 _ASSERTE(LastMethodOnGenericArrayInterfaces[i] < LastMethodOnGenericArrayInterfaces[i+1]);
5406 }
5407#endif
5408
5409 MethodTable* pExactMT = MscorlibBinder::GetClass(CLASS__SZARRAYHELPER);
5410
5411 // Subtract one from the non-generic IEnumerable that the generic IEnumerable<T>
5412 // inherits from.
5413 unsigned inheritanceDepth = pInterfaceMT->GetNumInterfaces() - 1;
5414 PREFIX_ASSUME(0 <= inheritanceDepth && inheritanceDepth < NumItems(LastMethodOnGenericArrayInterfaces));
5415
5416 // Read-only interfaces happen to always have one method
5417 bool fIsReadOnly = pInterfaceMT->GetNumVirtuals() == 1;
5418
5419 for(int i=0; i < (fIsReadOnly ? cReadOnlyMethods : cAllMethods); i++)
5420 {
5421 // Check whether the method applies for this type.
5422 if (SZArrayHelperMethodIDs[i] > LastMethodOnGenericArrayInterfaces[inheritanceDepth])
5423 continue;
5424
5425 MethodDesc * pPrimaryMD = MscorlibBinder::GetMethod(SZArrayHelperMethodIDs[i]);
5426
5427 MethodDesc * pInstantiatedMD = MethodDesc::FindOrCreateAssociatedMethodDesc(pPrimaryMD,
5428 pExactMT, false, Instantiation(&elemTypeHnd, 1), false);
5429
5430 TriageMethodForZap(pInstantiatedMD, true);
5431 }
5432}
5433
5434
5435void CEEPreloader::AddTypeToTransitiveClosureOfInstantiations(CORINFO_CLASS_HANDLE handle)
5436{
5437 STANDARD_VM_CONTRACT;
5438
5439 TriageTypeForZap((TypeHandle) handle, TRUE);
5440}
5441
5442const unsigned MAX_ZAP_INSTANTIATION_NESTING = 10;
5443
5444BOOL IsGenericTooDeeplyNested(TypeHandle t)
5445{
5446 CONTRACTL
5447 {
5448 NOTHROW;
5449 MODE_ANY;
5450 }
5451 CONTRACTL_END;
5452 //if this type is more than N levels nested deep, do not add it to the
5453 //closure. Build a queue for a DFS of the depth of instantiation.
5454
5455 //the current index in the queue we're visiting
5456 int currentQueueIdx; //use -1 to indicate that we're done.
5457 //the current generic arg type.
5458 TypeHandle currentVisitingType[MAX_ZAP_INSTANTIATION_NESTING];
5459
5460 //the ordinal in the GetInstantiation for the current type (over [0,
5461 //GetNumGenericArg())
5462 unsigned currentGenericArgEdge[MAX_ZAP_INSTANTIATION_NESTING];
5463
5464 //initialize the DFS.
5465 memset(currentGenericArgEdge, 0, sizeof(currentGenericArgEdge));
5466 currentVisitingType[0] = t;
5467 currentQueueIdx = 0;
5468
5469 while( currentQueueIdx >= 0 )
5470 {
5471 //see if we're done with this node
5472 if( currentVisitingType[currentQueueIdx].GetNumGenericArgs()
5473 <= currentGenericArgEdge[currentQueueIdx] )
5474 {
5475 --currentQueueIdx;
5476 }
5477 else
5478 {
5479 //more edges to visit. So visit one edge
5480 _ASSERTE(currentGenericArgEdge[currentQueueIdx] < currentVisitingType[currentQueueIdx].GetNumGenericArgs());
5481 TypeHandle current = currentVisitingType[currentQueueIdx].GetInstantiation()[currentGenericArgEdge[currentQueueIdx]];
5482 ++currentGenericArgEdge[currentQueueIdx];
5483 //only value types cause a problem because of "approximate" type
5484 //loading, so only worry about scanning value type arguments.
5485 if( current.HasInstantiation() && current.IsValueType() )
5486 {
5487 //new edge. Make sure there is space in the queue.
5488 if( (currentQueueIdx + 1) >= (int)NumItems(currentGenericArgEdge) )
5489 {
5490 //exceeded the allowable depth. Stop processing.
5491 return TRUE;
5492 }
5493 else
5494 {
5495 ++currentQueueIdx;
5496 currentGenericArgEdge[currentQueueIdx] = 0;
5497 currentVisitingType[currentQueueIdx] = current;
5498 }
5499 }
5500 }
5501 }
5502
5503 return FALSE;
5504}
5505
5506void CEEPreloader::TriageTypeForZap(TypeHandle th, BOOL fAcceptIfNotSure, BOOL fExpandDependencies)
5507{
5508 STANDARD_VM_CONTRACT;
5509
5510 // We care about param types only
5511 if (th.IsTypicalTypeDefinition() && !th.IsTypeDesc())
5512 return;
5513
5514 // We care about types from our module only
5515 if (m_image->GetModule() != th.GetLoaderModule())
5516 return;
5517
5518 // Check if we have decided to accept this type already.
5519 if (m_acceptedTypes.Lookup(th) != NULL)
5520 return;
5521
5522 // Check if we have decided to reject this type already.
5523 if (m_rejectedTypes.Lookup(th) != NULL)
5524 return;
5525
5526 enum { Investigate, Accepted, Rejected } triage = Investigate;
5527
5528 const char * rejectReason = NULL;
5529
5530 // TypeVarTypeDesc are saved via links from code:Module::m_GenericParamToDescMap
5531 if (th.IsGenericVariable())
5532 {
5533 triage = Rejected;
5534 rejectReason = "type is a Generic variable";
5535 goto Done;
5536 }
5537
5538 /* Consider this example:
5539
5540 class A<T> {}
5541 class B<U> : A<U> {}
5542
5543 class C<V> : B<V>
5544 {
5545 void foo<W>()
5546 {
5547 typeof(C<W>);
5548 typeof(B<A<W>>);
5549
5550 typeof(List<V>);
5551 }
5552 }
5553
5554 The open instantiations can be divided into the following 3 categories:
5555
5556 1. A<T>, B<U>, A<U>, C<V>, B<V>, A<V> are open instantiations involving
5557 ELEMENT_TYPE_VARs that need to be saved in the ngen image.
5558 2. List<V> is an instantiations that also involves ELEMENT_TYPE_VARs.
5559 However, it need not be saved since it will only be needed during the
5560 verification of foo<W>().
5561 3. C<W>, A<W>, B<A<W>> are open instantiations involving ELEMENT_TYPE_MVARs
5562 that need not be saved since they will only be needed during the
5563 verification of foo<W>().
5564
5565 Distinguishing between 1 and 2 requires walking C<V> and determining
5566 which ones are field/parent/interface types required by c<V>. However,
5567 category 3 is easy to detect, and can easily be pruned out. Hence,
5568 we pass in methodTypeVarsOnly=TRUE here.
5569 */
5570 if (th.ContainsGenericVariables(TRUE/*methodTypeVarsOnly*/))
5571 {
5572 triage = Rejected;
5573 rejectReason = "type contains method generic variables";
5574 goto Done;
5575 }
5576
5577 // Filter out weird cases we do not care about.
5578 if (!m_image->GetModule()->GetAvailableParamTypes()->ContainsValue(th))
5579 {
5580 triage = Rejected;
5581 rejectReason = "type is not in the current module";
5582 return;
5583 }
5584
5585 // Reject invalid generic instantiations. They will not be fully loaded
5586 // as they will throw a TypeLoadException before they reach CLASS_LOAD_LEVEL_FINAL.
5587 if (!th.IsFullyLoaded())
5588 {
5589 // This may load new types. May load new types.
5590 ClassLoader::TryEnsureLoaded(th);
5591
5592 if (!th.IsFullyLoaded())
5593 {
5594 triage = Rejected;
5595 rejectReason = "type could not be fully loaded, possibly because it does not satisfy its constraints";
5596 goto Done;
5597 }
5598 }
5599
5600 // Do not save any types containing generic class parameters from another module
5601 Module *pOpenModule;
5602 pOpenModule = th.GetDefiningModuleForOpenType();
5603 if (pOpenModule != NULL && pOpenModule != m_image->GetModule())
5604 {
5605 triage = Rejected;
5606 rejectReason = "type contains generic variables from another module";
5607 goto Done;
5608 }
5609
5610 // Always store items in their preferred zap module even if we are not sure
5611 if (Module::GetPreferredZapModuleForTypeHandle(th) == m_image->GetModule())
5612 {
5613 triage = Accepted;
5614 goto Done;
5615 }
5616
5617#ifdef FEATURE_FULL_NGEN
5618 // Only save arrays and other param types in their preferred zap modules,
5619 // i.e. never duplicate them.
5620 if (th.IsTypeDesc() || th.IsArrayType())
5621 {
5622 triage = Rejected;
5623 rejectReason = "type is a TypeDesc";
5624 goto Done;
5625 }
5626
5627 {
5628 // Do not save instantiations found in one of our hardbound dependencies
5629 PtrHashMap::PtrIterator iter = GetAppDomain()->ToCompilationDomain()->IterateHardBoundModules();
5630 for (/**/; !iter.end(); ++iter)
5631 {
5632 Module * hardBoundModule = (Module*)iter.GetValue();
5633 if (hardBoundModule->GetAvailableParamTypes()->ContainsValue(th))
5634 {
5635 triage = Rejected;
5636 rejectReason = "type was found in a hardbound dependency";
5637 goto Done;
5638 }
5639 }
5640 }
5641
5642 // We are not really sure about this type. Accept it only if we have been asked to.
5643 if (fAcceptIfNotSure)
5644 {
5645 if (!m_fSpeculativeTriage)
5646 {
5647 // We will take a look later before we actually start compiling the instantiations
5648 m_speculativeTypes.Append(th);
5649 m_acceptedTypes.Add(th);
5650 return;
5651 }
5652
5653 triage = Accepted;
5654 goto Done;
5655 }
5656#else
5657 rejectReason = "type is not in the preferred module";
5658 triage = Rejected;
5659#endif
5660
5661Done:
5662 switch (triage)
5663 {
5664 case Accepted:
5665 m_acceptedTypes.Add(th);
5666 if (fExpandDependencies)
5667 {
5668 ExpandTypeDependencies(th);
5669 }
5670 break;
5671
5672 case Rejected:
5673
5674 m_rejectedTypes.Add(th);
5675
5676#ifdef LOGGING
5677 // It is expensive to call th.GetName, only do it when we are actually logging
5678 if (LoggingEnabled())
5679 {
5680 SString typeName;
5681 th.GetName(typeName);
5682 LOG((LF_ZAP, LL_INFO10000, "TriageTypeForZap rejects %S (%08x) because %s\n",
5683 typeName.GetUnicode(), th.AsPtr(), rejectReason));
5684 }
5685#endif
5686 break;
5687
5688 default:
5689 // We have not found a compeling reason to accept or reject the type yet. Maybe next time...
5690 break;
5691 }
5692}
5693
5694void CEEPreloader::ExpandTypeDependencies(TypeHandle th)
5695{
5696 STANDARD_VM_CONTRACT;
5697
5698 if (th.IsTypeDesc())
5699 return;
5700
5701 MethodTable* pMT = th.AsMethodTable();
5702
5703 if (pMT->IsCanonicalMethodTable())
5704 {
5705 // Cutoff infinite recursion.
5706 if (!IsGenericTooDeeplyNested(th))
5707 {
5708 // Make sure all methods are compiled
5709 // We only want to check the method bodies owned by this type,
5710 // and not any method bodies owned by a parent type, as the
5711 // parent type may not get saved in this ngen image.
5712 MethodTable::IntroducedMethodIterator itr(pMT);
5713 for (/**/; itr.IsValid(); itr.Next())
5714 {
5715 AddToUncompiledMethods(itr.GetMethodDesc(), FALSE);
5716 }
5717 }
5718 }
5719 else
5720 {
5721 // Make sure canonical method table is saved
5722 TriageTypeForZap(pMT->GetCanonicalMethodTable(), TRUE);
5723 }
5724
5725 if (pMT->SupportsGenericInterop(TypeHandle::Interop_ManagedToNative))
5726 {
5727 MethodTable::IntroducedMethodIterator itr(pMT->GetCanonicalMethodTable());
5728 for (/**/; itr.IsValid(); itr.Next())
5729 {
5730 MethodDesc *pMD = itr.GetMethodDesc();
5731
5732 if (!pMD->HasMethodInstantiation())
5733 {
5734 if (pMT->IsInterface() || !pMD->IsSharedByGenericInstantiations())
5735 {
5736 pMD = MethodDesc::FindOrCreateAssociatedMethodDesc(
5737 pMD,
5738 pMT,
5739 FALSE, // forceBoxedEntryPoint
5740 Instantiation(), // methodInst
5741 FALSE, // allowInstParam
5742 TRUE); // forceRemotableMethod
5743 }
5744 else
5745 {
5746 _ASSERTE(pMT->IsDelegate());
5747 pMD = InstantiatedMethodDesc::FindOrCreateExactClassMethod(pMT, pMD);
5748 }
5749
5750 AddToUncompiledMethods(pMD, TRUE);
5751 }
5752 }
5753 }
5754
5755 // Make sure parent type is saved
5756 TriageTypeForZap(pMT->GetParentMethodTable(), TRUE);
5757
5758 // Make sure all instantiation arguments are saved
5759 Instantiation inst = pMT->GetInstantiation();
5760 for (DWORD iArg = 0; iArg < inst.GetNumArgs(); iArg++)
5761 {
5762 TriageTypeForZap(inst[iArg], TRUE);
5763 }
5764
5765 // Make sure all interfaces implemeted by the class are saved
5766 MethodTable::InterfaceMapIterator intIterator = pMT->IterateInterfaceMap();
5767 while (intIterator.Next())
5768 {
5769 TriageTypeForZap(intIterator.GetInterface(), TRUE);
5770 }
5771
5772 // Make sure approx types for all fields are saved
5773 ApproxFieldDescIterator fdIterator(pMT, ApproxFieldDescIterator::ALL_FIELDS);
5774 FieldDesc* pFD;
5775 while ((pFD = fdIterator.Next()) != NULL)
5776 {
5777 if (pFD->GetFieldType() == ELEMENT_TYPE_VALUETYPE)
5778 {
5779 TriageTypeForZap(pFD->GetFieldTypeHandleThrowing(), TRUE);
5780 }
5781 }
5782
5783 // Make sure types for all generic static fields are saved
5784
5785 if (pMT->HasGenericsStaticsInfo())
5786 {
5787 FieldDesc *pGenStaticFields = pMT->GetGenericsStaticFieldDescs();
5788 DWORD nFields = pMT->GetNumStaticFields();
5789 for (DWORD iField = 0; iField < nFields; iField++)
5790 {
5791 FieldDesc* pField = &pGenStaticFields[iField];
5792 if (pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE)
5793 {
5794 TriageTypeForZap(pField->GetFieldTypeHandleThrowing(), TRUE);
5795 }
5796 }
5797 }
5798
5799 // Expand type using the custom rules. May load new types.
5800 ApplyTypeDependencyProductionsForType(th);
5801}
5802
5803// Triage instantiations of generic methods
5804
5805void CEEPreloader::TriageMethodForZap(MethodDesc* pMD, BOOL fAcceptIfNotSure, BOOL fExpandDependencies)
5806{
5807 STANDARD_VM_CONTRACT;
5808
5809 // Submit the method type for triage
5810 TriageTypeForZap(TypeHandle(pMD->GetMethodTable()), fAcceptIfNotSure);
5811
5812 // We care about instantiated methods only
5813 if (pMD->IsTypicalMethodDefinition())
5814 return;
5815
5816 // We care about methods from our module only
5817 if (m_image->GetModule() != pMD->GetLoaderModule())
5818 return;
5819
5820 // Check if we have decided to accept this method already.
5821 if (m_acceptedMethods.Lookup(pMD) != NULL)
5822 return;
5823
5824 // Check if we have decided to reject this method already.
5825 if (m_rejectedMethods.Lookup(pMD) != NULL)
5826 return;
5827
5828 enum { Investigate, Accepted, Rejected } triage = Investigate;
5829
5830 const char * rejectReason = NULL;
5831
5832 // Do not save open methods
5833 if (pMD->ContainsGenericVariables())
5834 {
5835 triage = Rejected;
5836 rejectReason = "method contains method generic variables";
5837 goto Done;
5838 }
5839
5840 // Always store items in their preferred zap module even if we are not sure
5841 if (Module::GetPreferredZapModuleForMethodDesc(pMD) == m_image->GetModule())
5842 {
5843 triage = Accepted;
5844 goto Done;
5845 }
5846
5847#ifdef FEATURE_FULL_NGEN
5848 {
5849 // Do not save instantiations found in one of our hardbound dependencies
5850 PtrHashMap::PtrIterator iter = GetAppDomain()->ToCompilationDomain()->IterateHardBoundModules();
5851 for (/**/; !iter.end(); ++iter)
5852 {
5853 Module * hardBoundModule = (Module*)iter.GetValue();
5854 if (hardBoundModule->GetInstMethodHashTable()->ContainsMethodDesc(pMD))
5855 {
5856 triage = Rejected;
5857 rejectReason = "method was found in a hardbound dependency";
5858 goto Done;
5859 }
5860 }
5861 }
5862
5863 // We are not really sure about this method. Accept it only if we have been asked to.
5864 if (fAcceptIfNotSure)
5865 {
5866 // It does not seem worth it to go through extra hoops to eliminate redundant
5867 // speculative method instatiations from softbound dependencies like we do for types
5868 // if (!m_fSpeculativeTriage)
5869 // {
5870 // // We will take a look later before we actually start compiling the instantiations
5871 // ...
5872 // }
5873
5874 triage = Accepted;
5875 goto Done;
5876 }
5877#else
5878 triage = Rejected;
5879#endif
5880
5881Done:
5882 switch (triage)
5883 {
5884 case Accepted:
5885 m_acceptedMethods.Add(pMD);
5886 if (fExpandDependencies)
5887 {
5888 ExpandMethodDependencies(pMD);
5889 }
5890 break;
5891
5892 case Rejected:
5893 m_rejectedMethods.Add(pMD);
5894 LOG((LF_ZAP, LL_INFO10000, "TriageMethodForZap rejects %s (%08x) because %s\n",
5895 pMD->m_pszDebugMethodName, pMD, rejectReason));
5896 break;
5897
5898 default:
5899 // We have not found a compeling reason to accept or reject the method yet. Maybe next time...
5900 break;
5901 }
5902}
5903
5904void CEEPreloader::ExpandMethodDependencies(MethodDesc * pMD)
5905{
5906 STANDARD_VM_CONTRACT;
5907
5908 AddToUncompiledMethods(pMD, FALSE);
5909
5910 {
5911 // Make sure all instantiation arguments are saved
5912 Instantiation inst = pMD->GetMethodInstantiation();
5913 for (DWORD iArg = 0; iArg < inst.GetNumArgs(); iArg++)
5914 {
5915 TriageTypeForZap(inst[iArg], TRUE);
5916 }
5917 }
5918
5919 // Make sure to add wrapped method desc
5920 if (pMD->IsWrapperStub())
5921 TriageMethodForZap(pMD->GetWrappedMethodDesc(), TRUE);
5922}
5923
5924void CEEPreloader::TriageTypeFromSoftBoundModule(TypeHandle th, Module * pSoftBoundModule)
5925{
5926 STANDARD_VM_CONTRACT;
5927
5928 // We care about types from our module only
5929 if (m_image->GetModule() != th.GetLoaderModule())
5930 return;
5931
5932 // Nothing to do if we have rejected the type already.
5933 if (m_rejectedTypes.Lookup(th) != NULL)
5934 return;
5935
5936 // We make guarantees about types living in its own PZM only
5937 if (Module::GetPreferredZapModuleForTypeHandle(th) != pSoftBoundModule)
5938 return;
5939
5940 // Reject the type - it is guaranteed to be saved in PZM
5941 m_rejectedTypes.Add(th);
5942
5943 if (!th.IsTypeDesc())
5944 {
5945 // Reject the canonical method table if possible.
5946 MethodTable* pMT = th.AsMethodTable();
5947 if (!pMT->IsCanonicalMethodTable())
5948 TriageTypeFromSoftBoundModule(pMT->GetCanonicalMethodTable(), pSoftBoundModule);
5949
5950 // Reject parent method table if possible.
5951 TriageTypeFromSoftBoundModule(pMT->GetParentMethodTable(), pSoftBoundModule);
5952
5953 // Reject all interfaces implemented by the type if possible.
5954 MethodTable::InterfaceMapIterator intIterator = pMT->IterateInterfaceMap();
5955 while (intIterator.Next())
5956 {
5957 TriageTypeFromSoftBoundModule(intIterator.GetInterface(), pSoftBoundModule);
5958 }
5959
5960 // It does not seem worth it to reject the remaining items
5961 // expanded by CEEPreloader::ExpandTypeDependencies here.
5962 }
5963}
5964
5965#ifdef FEATURE_FULL_NGEN
5966static TypeHandle TryToLoadTypeSpecHelper(Module * pModule, PCCOR_SIGNATURE pSig, ULONG cSig)
5967{
5968 STANDARD_VM_CONTRACT;
5969
5970 TypeHandle th;
5971
5972 EX_TRY
5973 {
5974 SigPointer p(pSig, cSig);
5975 SigTypeContext typeContext; // empty context is OK: encoding should not contain type variables.
5976
5977 th = p.GetTypeHandleThrowing(pModule, &typeContext, ClassLoader::DontLoadTypes);
5978 }
5979 EX_CATCH
5980 {
5981 }
5982 EX_END_CATCH(SwallowAllExceptions)
5983
5984 return th;
5985}
5986
5987void CEEPreloader::TriageTypeSpecsFromSoftBoundModule(Module * pSoftBoundModule)
5988{
5989 STANDARD_VM_CONTRACT;
5990
5991 //
5992 // Reject all typespecs that are guranteed to be found in soft bound PZM
5993 //
5994
5995 IMDInternalImport *pInternalImport = pSoftBoundModule->GetMDImport();
5996
5997 HENUMInternalHolder hEnum(pInternalImport);
5998 hEnum.EnumAllInit(mdtTypeSpec);
5999
6000 mdToken tk;
6001 while (pInternalImport->EnumNext(&hEnum, &tk))
6002 {
6003 ULONG cSig;
6004 PCCOR_SIGNATURE pSig;
6005
6006 if (FAILED(pInternalImport->GetTypeSpecFromToken(tk, &pSig, &cSig)))
6007 {
6008 pSig = NULL;
6009 cSig = 0;
6010 }
6011
6012 // Check all types specs that do not contain variables
6013 if (SigPointer(pSig, cSig).IsPolyType(NULL) == hasNoVars)
6014 {
6015 TypeHandle th = TryToLoadTypeSpecHelper(pSoftBoundModule, pSig, cSig);
6016
6017 if (th.IsNull())
6018 continue;
6019
6020 TriageTypeFromSoftBoundModule(th, pSoftBoundModule);
6021 }
6022 }
6023}
6024
6025void CEEPreloader::TriageSpeculativeType(TypeHandle th)
6026{
6027 STANDARD_VM_CONTRACT;
6028
6029 // Nothing to do if we have rejected the type already
6030 if (m_rejectedTypes.Lookup(th) != NULL)
6031 return;
6032
6033 Module * pPreferredZapModule = Module::GetPreferredZapModuleForTypeHandle(th);
6034 BOOL fHardBoundPreferredZapModule = FALSE;
6035
6036 //
6037 // Even though we have done this check already earlier, do it again here in case we have picked up
6038 // any eager-bound dependency in the meantime
6039 //
6040 // Do not save instantiations found in one of our eager-bound dependencies
6041 PtrHashMap::PtrIterator iter = GetAppDomain()->ToCompilationDomain()->IterateHardBoundModules();
6042 for (/**/; !iter.end(); ++iter)
6043 {
6044 Module * hardBoundModule = (Module*)iter.GetValue();
6045 if (hardBoundModule->GetAvailableParamTypes()->ContainsValue(th))
6046 {
6047 m_rejectedTypes.Add(th);
6048 return;
6049 }
6050
6051 if (hardBoundModule == pPreferredZapModule)
6052 {
6053 fHardBoundPreferredZapModule = TRUE;
6054 }
6055 }
6056
6057 if (!fHardBoundPreferredZapModule && !pPreferredZapModule->AreTypeSpecsTriaged())
6058 {
6059 // Reject all types that are guaranteed to be instantiated in soft bound PZM
6060 TriageTypeSpecsFromSoftBoundModule(pPreferredZapModule);
6061 pPreferredZapModule->SetTypeSpecsTriaged();
6062
6063 if (m_rejectedTypes.Lookup(th) != NULL)
6064 return;
6065 }
6066
6067 // We have to no other option but to accept and expand the type
6068 ExpandTypeDependencies(th);
6069}
6070
6071void CEEPreloader::TriageSpeculativeInstantiations()
6072{
6073 STANDARD_VM_CONTRACT;
6074
6075 // Get definitive triage answer for speculative types that we have run into earlier
6076 // Note that m_speculativeTypes may be growing as this loop runs
6077 for (COUNT_T i = 0; i < m_speculativeTypes.GetCount(); i++)
6078 {
6079 TriageSpeculativeType(m_speculativeTypes[i]);
6080 }
6081
6082 // We are done - the array of speculative types is no longer necessary
6083 m_speculativeTypes.Clear();
6084}
6085#endif // FEATURE_FULL_NGEN
6086
6087BOOL CEEPreloader::TriageForZap(BOOL fAcceptIfNotSure, BOOL fExpandDependencies)
6088{
6089 STANDARD_VM_CONTRACT;
6090
6091 DWORD dwNumTypes = m_image->GetModule()->GetAvailableParamTypes()->GetCount();
6092 DWORD dwNumMethods = m_image->GetModule()->GetInstMethodHashTable()->GetCount();
6093
6094 // Triage types
6095 {
6096 // Create a local copy in case the new elements are added to the hashtable during population
6097 InlineSArray<TypeHandle, 20> pTypes;
6098
6099 // Make sure the iterator is destroyed before there is a chance of loading new types
6100 {
6101 EETypeHashTable* pTable = m_image->GetModule()->GetAvailableParamTypes();
6102
6103 EETypeHashTable::Iterator it(pTable);
6104 EETypeHashEntry *pEntry;
6105 while (pTable->FindNext(&it, &pEntry))
6106 {
6107 TypeHandle th = pEntry->GetTypeHandle();
6108 if (m_acceptedTypes.Lookup(th) == NULL && m_rejectedTypes.Lookup(th) == NULL)
6109 pTypes.Append(th);
6110 }
6111 }
6112
6113 for(COUNT_T i = 0; i < pTypes.GetCount(); i ++)
6114 {
6115 TriageTypeForZap(pTypes[i], fAcceptIfNotSure, fExpandDependencies);
6116 }
6117 }
6118
6119 // Triage methods
6120 {
6121 // Create a local copy in case the new elements are added to the hashtable during population
6122 InlineSArray<MethodDesc*, 20> pMethods;
6123
6124 // Make sure the iterator is destroyed before there is a chance of loading new methods
6125 {
6126 InstMethodHashTable* pTable = m_image->GetModule()->GetInstMethodHashTable();
6127
6128 InstMethodHashTable::Iterator it(pTable);
6129 InstMethodHashEntry *pEntry;
6130 while (pTable->FindNext(&it, &pEntry))
6131 {
6132 MethodDesc* pMD = pEntry->GetMethod();
6133 if (m_acceptedMethods.Lookup(pMD) == NULL && m_rejectedMethods.Lookup(pMD) == NULL)
6134 pMethods.Append(pMD);
6135 }
6136 }
6137
6138 for(COUNT_T i = 0; i < pMethods.GetCount(); i ++)
6139 {
6140 TriageMethodForZap(pMethods[i], fAcceptIfNotSure, fExpandDependencies);
6141 }
6142 }
6143
6144 // Returns TRUE if new types or methods has been added by the triage
6145 return (dwNumTypes != m_image->GetModule()->GetAvailableParamTypes()->GetCount()) ||
6146 (dwNumMethods != m_image->GetModule()->GetInstMethodHashTable()->GetCount());
6147}
6148
6149void CEEPreloader::PrePrepareMethodIfNecessary(CORINFO_METHOD_HANDLE hMethod)
6150{
6151 STANDARD_VM_CONTRACT;
6152
6153}
6154
6155static void SetStubMethodDescOnInteropMethodDesc(MethodDesc* pInteropMD, MethodDesc* pStubMD, bool fReverseStub)
6156{
6157 CONTRACTL
6158 {
6159 THROWS;
6160 GC_TRIGGERS;
6161 MODE_ANY;
6162
6163 // We store NGENed stubs on these MethodDesc types
6164 PRECONDITION(pInteropMD->IsNDirect() || pInteropMD->IsComPlusCall() || pInteropMD->IsGenericComPlusCall() || pInteropMD->IsEEImpl());
6165 }
6166 CONTRACTL_END;
6167
6168 if (pInteropMD->IsNDirect())
6169 {
6170 _ASSERTE(!fReverseStub);
6171 NDirectMethodDesc* pNMD = (NDirectMethodDesc*)pInteropMD;
6172 pNMD->ndirect.m_pStubMD.SetValue(pStubMD);
6173 }
6174#ifdef FEATURE_COMINTEROP
6175 else if (pInteropMD->IsComPlusCall() || pInteropMD->IsGenericComPlusCall())
6176 {
6177 _ASSERTE(!fReverseStub);
6178 ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pInteropMD);
6179 pComInfo->m_pStubMD.SetValue(pStubMD);
6180 }
6181#endif // FEATURE_COMINTEROP
6182 else if (pInteropMD->IsEEImpl())
6183 {
6184 DelegateEEClass* pDelegateClass = (DelegateEEClass*)pInteropMD->GetClass();
6185 if (fReverseStub)
6186 {
6187 pDelegateClass->m_pReverseStubMD = pStubMD;
6188 }
6189 else
6190 {
6191#ifdef FEATURE_COMINTEROP
6192 // We don't currently NGEN both the P/Invoke and WinRT stubs for WinRT delegates.
6193 // If that changes, this function will need an extra parameter to tell what kind
6194 // of stub is being passed.
6195 if (pInteropMD->GetMethodTable()->IsWinRTDelegate())
6196 {
6197 pDelegateClass->m_pComPlusCallInfo->m_pStubMD.SetValue(pStubMD);
6198 }
6199 else
6200#endif // FEATURE_COMINTEROP
6201 {
6202 pDelegateClass->m_pForwardStubMD = pStubMD;
6203 }
6204 }
6205 }
6206 else
6207 {
6208 UNREACHABLE_MSG("unexpected type of MethodDesc");
6209 }
6210}
6211
6212MethodDesc * CEEPreloader::CompileMethodStubIfNeeded(
6213 MethodDesc *pMD,
6214 MethodDesc *pStubMD,
6215 ICorCompilePreloader::CORCOMPILE_CompileStubCallback pfnCallback,
6216 LPVOID pCallbackContext)
6217{
6218 STANDARD_VM_CONTRACT;
6219
6220 LOG((LF_ZAP, LL_INFO10000, "NGEN_ILSTUB: %s::%s -> %s::%s\n",
6221 pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName, pStubMD->m_pszDebugClassName, pStubMD->m_pszDebugMethodName));
6222
6223 // It is possible that the StubMD is a normal method pointed by InteropStubMethodAttribute,
6224 // and in that case we don't need to compile it here
6225 if (pStubMD->IsDynamicMethod())
6226 {
6227 if (!pStubMD->AsDynamicMethodDesc()->GetILStubResolver()->IsCompiled())
6228 {
6229 CORJIT_FLAGS jitFlags = pStubMD->AsDynamicMethodDesc()->GetILStubResolver()->GetJitFlags();
6230
6231 pfnCallback(pCallbackContext, (CORINFO_METHOD_HANDLE)pStubMD, jitFlags);
6232 }
6233
6234#ifndef FEATURE_FULL_NGEN // Deduplication
6235 const DuplicateMethodEntry * pDuplicate = m_duplicateMethodsHash.LookupPtr(pStubMD);
6236 if (pDuplicate != NULL)
6237 return pDuplicate->pDuplicateMD;
6238#endif
6239 }
6240
6241//We do not store ILStubs so if the compilation failed for them
6242//It does not make sense to keep the MD corresponding to the IL
6243 if (pStubMD->IsILStub() && m_image->GetCodeAddress(pStubMD) == NULL)
6244 pStubMD=NULL;
6245
6246 return pStubMD;
6247}
6248
6249void CEEPreloader::GenerateMethodStubs(
6250 CORINFO_METHOD_HANDLE hMethod,
6251 bool fNgenProfilerImage,
6252 CORCOMPILE_CompileStubCallback pfnCallback,
6253 LPVOID pCallbackContext)
6254{
6255 CONTRACTL
6256 {
6257 STANDARD_VM_CHECK;
6258 PRECONDITION(hMethod != NULL && pfnCallback != NULL);
6259 }
6260 CONTRACTL_END;
6261
6262 MethodDesc* pMD = GetMethod(hMethod);
6263 MethodDesc* pStubMD = NULL;
6264
6265 // Do not generate IL stubs when generating ReadyToRun images
6266 // This prevents versionability concerns around IL stubs exposing internal
6267 // implementation details of the CLR.
6268 if (IsReadyToRunCompilation())
6269 return;
6270
6271 DWORD dwNGenStubFlags = NDIRECTSTUB_FL_NGENEDSTUB;
6272
6273 if (fNgenProfilerImage)
6274 dwNGenStubFlags |= NDIRECTSTUB_FL_NGENEDSTUBFORPROFILING;
6275
6276 //
6277 // Generate IL stubs. If failed, we go through normal NGEN path
6278 // Catch any exceptions that occur when we try to create the IL_STUB
6279 //
6280 EX_TRY
6281 {
6282 //
6283 // Take care of forward stubs
6284 //
6285 if (pMD->IsNDirect())
6286 {
6287 NDirectMethodDesc* pNMD = (NDirectMethodDesc*)pMD;
6288 PInvokeStaticSigInfo sigInfo;
6289 NDirect::PopulateNDirectMethodDesc(pNMD, &sigInfo);
6290 pStubMD = NDirect::GetILStubMethodDesc((NDirectMethodDesc*)pMD, &sigInfo, dwNGenStubFlags);
6291 }
6292#ifdef FEATURE_COMINTEROP
6293 else if (pMD->IsComPlusCall() || pMD->IsGenericComPlusCall())
6294 {
6295 if (MethodNeedsForwardComStub(pMD, m_image))
6296 {
6297 // Look for predefined IL stubs in forward com interop scenario.
6298 // If we've found a stub, that's what we'll use
6299 DWORD dwStubFlags;
6300 ComPlusCall::PopulateComPlusCallMethodDesc(pMD, &dwStubFlags);
6301 if (FAILED(FindPredefinedILStubMethod(pMD, dwStubFlags, &pStubMD)))
6302 {
6303 pStubMD = ComPlusCall::GetILStubMethodDesc(pMD, dwStubFlags | dwNGenStubFlags);
6304 }
6305 }
6306 }
6307#endif // FEATURE_COMINTEROP
6308 else if (pMD->IsEEImpl())
6309 {
6310 MethodTable* pMT = pMD->GetMethodTable();
6311 CONSISTENCY_CHECK(pMT->IsDelegate());
6312
6313 // we can filter out non-WinRT generic delegates right off the top
6314 if (!pMD->HasClassOrMethodInstantiation() || pMT->IsProjectedFromWinRT()
6315#ifdef FEATURE_COMINTEROP
6316 || WinRTTypeNameConverter::IsRedirectedType(pMT)
6317#endif // FEATURE_COMINTEROP
6318 )
6319 {
6320 if (COMDelegate::IsDelegateInvokeMethod(pMD)) // build forward stub
6321 {
6322#ifdef FEATURE_COMINTEROP
6323 if ((pMT->IsProjectedFromWinRT() || WinRTTypeNameConverter::IsRedirectedType(pMT)) &&
6324 (!pMT->HasInstantiation() || pMT->SupportsGenericInterop(TypeHandle::Interop_ManagedToNative))) // filter out shared generics
6325 {
6326 // Build the stub for all WinRT delegates, these will definitely be used for interop.
6327 if (pMT->IsLegalNonArrayWinRTType())
6328 {
6329 COMDelegate::PopulateComPlusCallInfo(pMT);
6330 pStubMD = COMDelegate::GetILStubMethodDesc((EEImplMethodDesc *)pMD, dwNGenStubFlags);
6331 }
6332 }
6333 else
6334#endif // FEATURE_COMINTEROP
6335 {
6336 // Build the stub only if the delegate is decorated with UnmanagedFunctionPointerAttribute.
6337 // Forward delegate stubs are rare so we require this opt-in to avoid bloating NGEN images.
6338
6339 if (S_OK == pMT->GetMDImport()->GetCustomAttributeByName(
6340 pMT->GetCl(), g_UnmanagedFunctionPointerAttribute, NULL, NULL))
6341 {
6342 pStubMD = COMDelegate::GetILStubMethodDesc((EEImplMethodDesc *)pMD, dwNGenStubFlags);
6343 }
6344 }
6345 }
6346 }
6347 }
6348
6349 // compile the forward stub
6350 if (pStubMD != NULL)
6351 {
6352 pStubMD = CompileMethodStubIfNeeded(pMD, pStubMD, pfnCallback, pCallbackContext);
6353
6354 // We store the MethodDesc of the Stub on the NDirectMethodDesc/ComPlusCallMethodDesc/DelegateEEClass
6355 // that we can recover the stub MethodDesc at prestub time, do the fixups, and wire up the native code
6356 if (pStubMD != NULL)
6357 {
6358 SetStubMethodDescOnInteropMethodDesc(pMD, pStubMD, false /* fReverseStub */);
6359 pStubMD = NULL;
6360 }
6361
6362 }
6363 }
6364 EX_CATCH
6365 {
6366 LOG((LF_ZAP, LL_WARNING, "NGEN_ILSTUB: Generating forward interop stub FAILED: %s::%s\n", pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
6367 }
6368 EX_END_CATCH(RethrowTransientExceptions);
6369
6370 //
6371 // Now take care of reverse P/Invoke stubs for delegates
6372 //
6373 if (pMD->IsEEImpl() && COMDelegate::IsDelegateInvokeMethod(pMD))
6374 {
6375 // Reverse P/Invoke is not supported for generic methods and WinRT delegates
6376 if (!pMD->HasClassOrMethodInstantiation() && !pMD->GetMethodTable()->IsProjectedFromWinRT())
6377 {
6378 EX_TRY
6379 {
6380#ifdef _TARGET_X86_
6381 // on x86, we call the target directly if Invoke has a no-marshal signature
6382 if (NDirect::MarshalingRequired(pMD))
6383#endif // _TARGET_X86_
6384 {
6385 PInvokeStaticSigInfo sigInfo(pMD);
6386 pStubMD = UMThunkMarshInfo::GetILStubMethodDesc(pMD, &sigInfo, NDIRECTSTUB_FL_DELEGATE | dwNGenStubFlags);
6387
6388 if (pStubMD != NULL)
6389 {
6390 // compile the reverse stub
6391 pStubMD = CompileMethodStubIfNeeded(pMD, pStubMD, pfnCallback, pCallbackContext);
6392
6393 // We store the MethodDesc of the Stub on the DelegateEEClass
6394 if (pStubMD != NULL)
6395 {
6396 SetStubMethodDescOnInteropMethodDesc(pMD, pStubMD, true /* fReverseStub */);
6397 }
6398 }
6399 }
6400 }
6401 EX_CATCH
6402 {
6403 LOG((LF_ZAP, LL_WARNING, "NGEN_ILSTUB: Generating reverse interop stub for delegate FAILED: %s::%s\n", pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
6404 }
6405 EX_END_CATCH(RethrowTransientExceptions);
6406 }
6407 }
6408
6409#ifdef FEATURE_COMINTEROP
6410 //
6411 // And finally generate reverse COM stubs
6412 //
6413 EX_TRY
6414 {
6415 // The method doesn't have to have a special type to be exposed to COM, in particular it doesn't
6416 // have to be ComPlusCallMethodDesc. However, it must have certain properties (custom attributes,
6417 // public visibility, etc.)
6418 if (MethodNeedsReverseComStub(pMD))
6419 {
6420 // initialize ComCallMethodDesc
6421 ComCallMethodDesc ccmd;
6422 ComCallMethodDescHolder ccmdHolder(&ccmd);
6423 ccmd.InitMethod(pMD, NULL);
6424
6425 // generate the IL stub
6426 DWORD dwStubFlags;
6427 ComCall::PopulateComCallMethodDesc(&ccmd, &dwStubFlags);
6428 pStubMD = ComCall::GetILStubMethodDesc(pMD, dwStubFlags | dwNGenStubFlags);
6429
6430 if (pStubMD != NULL)
6431 {
6432 // compile the reverse stub
6433 pStubMD = CompileMethodStubIfNeeded(pMD, pStubMD, pfnCallback, pCallbackContext);
6434
6435 if (pStubMD != NULL)
6436 {
6437 // store the stub in a hash table on the module
6438 m_image->GetModule()->GetStubMethodHashTable()->InsertMethodDesc(pMD, pStubMD);
6439 }
6440 }
6441 }
6442 }
6443 EX_CATCH
6444 {
6445 LOG((LF_ZAP, LL_WARNING, "NGEN_ILSTUB: Generating reverse interop stub FAILED: %s::%s\n", pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName));
6446 }
6447 EX_END_CATCH(RethrowTransientExceptions);
6448#endif // FEATURE_COMINTEROP
6449}
6450
6451bool CEEPreloader::IsDynamicMethod(CORINFO_METHOD_HANDLE hMethod)
6452{
6453 STANDARD_VM_CONTRACT;
6454
6455 MethodDesc* pMD = GetMethod(hMethod);
6456
6457 if (pMD)
6458 {
6459 return pMD->IsDynamicMethod();
6460 }
6461
6462 return false;
6463}
6464
6465// Set method profiling flags for layout of EE datastructures
6466void CEEPreloader::SetMethodProfilingFlags(CORINFO_METHOD_HANDLE hMethod, DWORD flags)
6467{
6468 STANDARD_VM_CONTRACT;
6469
6470 _ASSERTE(hMethod != NULL);
6471 _ASSERTE(flags != 0);
6472
6473 return m_image->SetMethodProfilingFlags(GetMethod(hMethod), flags);
6474}
6475
6476/*********************************************************************/
6477// canSkipMethodPreparation: Is there a need for all calls from
6478// NGEN'd code to a particular MethodDesc to go through DoPrestub,
6479// depending on the method sematics? If so return FALSE.
6480//
6481// This is used to rule out both ngen-hardbinds and intra-ngen-module
6482// direct calls.
6483//
6484// The cases where direct calls are not allowed are typically where
6485// a stub must be inserted by DoPrestub (we do not save stubs) or where
6486// we haven't saved the code for some reason or another, or where fixups
6487// are required in the MethodDesc.
6488//
6489// callerHnd=NULL implies any/unspecified caller.
6490//
6491// Note that there may be other requirements for going through the prestub
6492// which vary based on the scenario. These need to be handled separately
6493
6494bool CEEPreloader::CanSkipMethodPreparation (
6495 CORINFO_METHOD_HANDLE callerHnd,
6496 CORINFO_METHOD_HANDLE calleeHnd,
6497 CorInfoIndirectCallReason *pReason,
6498 CORINFO_ACCESS_FLAGS accessFlags/*=CORINFO_ACCESS_ANY*/)
6499{
6500 STANDARD_VM_CONTRACT;
6501
6502 bool result = false;
6503
6504 COOPERATIVE_TRANSITION_BEGIN();
6505
6506 MethodDesc * calleeMD = (MethodDesc *)calleeHnd;
6507 MethodDesc * callerMD = (MethodDesc *)callerHnd;
6508
6509 {
6510 result = calleeMD->CanSkipDoPrestub(callerMD, pReason, accessFlags);
6511 }
6512
6513 COOPERATIVE_TRANSITION_END();
6514
6515 return result;
6516}
6517
6518CORINFO_METHOD_HANDLE CEEPreloader::LookupMethodDef(mdMethodDef token)
6519{
6520 STANDARD_VM_CONTRACT;
6521 MethodDesc *resultMD = nullptr;
6522
6523 EX_TRY
6524 {
6525 MethodDesc *pMD = MemberLoader::GetMethodDescFromMethodDef(m_image->GetModule(), token, FALSE);
6526
6527 if (IsReadyToRunCompilation() && pMD->HasClassOrMethodInstantiation())
6528 {
6529 _ASSERTE(IsCompilationProcess() && pMD->GetModule_NoLogging() == GetAppDomain()->ToCompilationDomain()->GetTargetModule());
6530 }
6531
6532 resultMD = pMD->FindOrCreateTypicalSharedInstantiation();
6533 }
6534 EX_CATCH
6535 {
6536 this->Error(token, GET_EXCEPTION());
6537 }
6538 EX_END_CATCH(SwallowAllExceptions)
6539
6540 return CORINFO_METHOD_HANDLE(resultMD);
6541}
6542
6543bool CEEPreloader::GetMethodInfo(mdMethodDef token, CORINFO_METHOD_HANDLE ftnHnd, CORINFO_METHOD_INFO * methInfo)
6544{
6545 STANDARD_VM_CONTRACT;
6546 bool result = false;
6547
6548 EX_TRY
6549 {
6550 result = GetZapJitInfo()->getMethodInfo(ftnHnd, methInfo);
6551 }
6552 EX_CATCH
6553 {
6554 result = false;
6555 this->Error(token, GET_EXCEPTION());
6556 }
6557 EX_END_CATCH(SwallowAllExceptions)
6558
6559 return result;
6560}
6561
6562static BOOL MethodIsVisibleOutsideItsAssembly(DWORD dwMethodAttr)
6563{
6564 LIMITED_METHOD_CONTRACT;
6565 return (IsMdPublic(dwMethodAttr) ||
6566 IsMdFamORAssem(dwMethodAttr) ||
6567 IsMdFamily(dwMethodAttr));
6568}
6569
6570static BOOL ClassIsVisibleOutsideItsAssembly(DWORD dwClassAttr, BOOL fIsGlobalClass)
6571{
6572 LIMITED_METHOD_CONTRACT;
6573
6574 if (fIsGlobalClass)
6575 {
6576 return TRUE;
6577 }
6578
6579 return (IsTdPublic(dwClassAttr) ||
6580 IsTdNestedPublic(dwClassAttr) ||
6581 IsTdNestedFamily(dwClassAttr) ||
6582 IsTdNestedFamORAssem(dwClassAttr));
6583}
6584
6585static BOOL MethodIsVisibleOutsideItsAssembly(MethodDesc * pMD)
6586{
6587 LIMITED_METHOD_CONTRACT;
6588
6589 MethodTable * pMT = pMD->GetMethodTable();
6590
6591 if (!ClassIsVisibleOutsideItsAssembly(pMT->GetAttrClass(), pMT->IsGlobalClass()))
6592 return FALSE;
6593
6594 return MethodIsVisibleOutsideItsAssembly(pMD->GetAttrs());
6595}
6596
6597CorCompileILRegion CEEPreloader::GetILRegion(mdMethodDef token)
6598{
6599 STANDARD_VM_CONTRACT;
6600
6601 // Since we are running managed code during NGen the inlining hint may be
6602 // changing underneeth us as the code is JITed. We need to prevent the inlining
6603 // hints from changing once we start to use them to place IL in the image.
6604 g_pCEECompileInfo->DisableCachingOfInliningHints();
6605
6606 // Default if there is something completely wrong, e.g. the type failed to load.
6607 // We may need the IL at runtime.
6608 CorCompileILRegion region = CORCOMPILE_ILREGION_WARM;
6609
6610 EX_TRY
6611 {
6612 MethodDesc *pMD = m_image->GetModule()->LookupMethodDef(token);
6613
6614 if (pMD == NULL || !pMD->GetMethodTable()->IsFullyLoaded())
6615 {
6616 // Something is completely wrong - use the default
6617 }
6618 else
6619 if (m_image->IsStored(pMD))
6620 {
6621 if (pMD->IsNotInline())
6622 {
6623 if (pMD->HasClassOrMethodInstantiation())
6624 {
6625 region = CORCOMPILE_ILREGION_GENERICS;
6626 }
6627 else
6628 {
6629 region = CORCOMPILE_ILREGION_COLD;
6630 }
6631 }
6632 else
6633 if (MethodIsVisibleOutsideItsAssembly(pMD))
6634 {
6635 // We are inlining only leaf methods, except for mscorlib. Thus we can assume that only methods
6636 // visible outside its assembly are likely to be inlined.
6637 region = CORCOMPILE_ILREGION_INLINEABLE;
6638 }
6639 else
6640 {
6641 // We may still need the IL of the non-nonvisible methods for inlining in certain scenarios:
6642 // dynamically emitted IL, friend assemblies or JITing of generic instantiations
6643 region = CORCOMPILE_ILREGION_WARM;
6644 }
6645 }
6646 }
6647 EX_CATCH
6648 {
6649 }
6650 EX_END_CATCH(SwallowAllExceptions)
6651
6652 return region;
6653}
6654
6655
6656CORINFO_METHOD_HANDLE CEEPreloader::FindMethodForProfileEntry(CORBBTPROF_BLOB_PARAM_SIG_ENTRY * profileBlobEntry)
6657{
6658 STANDARD_VM_CONTRACT;
6659 MethodDesc * pMethod = nullptr;
6660
6661 _ASSERTE(profileBlobEntry->blob.type == ParamMethodSpec);
6662
6663 if (PartialNGenStressPercentage() != 0)
6664 return CORINFO_METHOD_HANDLE( NULL );
6665
6666 Module * pModule = GetAppDomain()->ToCompilationDomain()->GetTargetModule();
6667 pMethod = pModule->LoadIBCMethodHelper(m_image, profileBlobEntry);
6668
6669 return CORINFO_METHOD_HANDLE( pMethod );
6670}
6671
6672void CEEPreloader::ReportInlining(CORINFO_METHOD_HANDLE inliner, CORINFO_METHOD_HANDLE inlinee)
6673{
6674 STANDARD_VM_CONTRACT;
6675 m_image->ReportInlining(inliner, inlinee);
6676}
6677
6678void CEEPreloader::Link()
6679{
6680 STANDARD_VM_CONTRACT;
6681
6682 COOPERATIVE_TRANSITION_BEGIN();
6683
6684 m_image->PreSave();
6685
6686 m_image->GetModule()->Save(m_image);
6687 m_image->GetModule()->Arrange(m_image);
6688 m_image->GetModule()->Fixup(m_image);
6689
6690 m_image->PostSave();
6691
6692 COOPERATIVE_TRANSITION_END();
6693}
6694
6695void CEEPreloader::FixupRVAs()
6696{
6697 STANDARD_VM_CONTRACT;
6698
6699 COOPERATIVE_TRANSITION_BEGIN();
6700
6701 m_image->FixupRVAs();
6702
6703 COOPERATIVE_TRANSITION_END();
6704}
6705
6706void CEEPreloader::SetRVAsForFields(IMetaDataEmit * pEmit)
6707{
6708 STANDARD_VM_CONTRACT;
6709
6710 COOPERATIVE_TRANSITION_BEGIN();
6711
6712 m_image->SetRVAsForFields(pEmit);
6713
6714 COOPERATIVE_TRANSITION_END();
6715}
6716
6717void CEEPreloader::GetRVAFieldData(mdFieldDef fd, PVOID * ppData, DWORD * pcbSize, DWORD * pcbAlignment)
6718{
6719 STANDARD_VM_CONTRACT;
6720
6721 COOPERATIVE_TRANSITION_BEGIN();
6722
6723 FieldDesc * pFD = m_image->GetModule()->LookupFieldDef(fd);
6724 if (pFD == NULL)
6725 ThrowHR(COR_E_TYPELOAD);
6726
6727 _ASSERTE(pFD->IsRVA());
6728
6729 UINT size = pFD->LoadSize();
6730
6731 //
6732 // Compute an alignment for the data based on the alignment
6733 // of the RVA. We'll align up to 8 bytes.
6734 //
6735
6736 UINT align = 1;
6737 DWORD rva = pFD->GetOffset();
6738 DWORD rvaTemp = rva;
6739
6740 while ((rvaTemp&1) == 0 && align < 8 && align < size)
6741 {
6742 align <<= 1;
6743 rvaTemp >>= 1;
6744 }
6745
6746
6747 *ppData = pFD->GetStaticAddressHandle(NULL);
6748 *pcbSize = size;
6749 *pcbAlignment = align;
6750
6751 COOPERATIVE_TRANSITION_END();
6752}
6753
6754ULONG CEEPreloader::Release()
6755{
6756 CONTRACTL {
6757 NOTHROW;
6758 GC_NOTRIGGER;
6759 MODE_ANY;
6760 } CONTRACTL_END;
6761
6762 delete this;
6763 return 0;
6764}
6765
6766#ifdef FEATURE_READYTORUN_COMPILER
6767void CEEPreloader::GetSerializedInlineTrackingMap(SBuffer* pBuffer)
6768{
6769 InlineTrackingMap * pInlineTrackingMap = m_image->GetInlineTrackingMap();
6770 PersistentInlineTrackingMapR2R::Save(m_image->GetHeap(), pBuffer, pInlineTrackingMap);
6771}
6772#endif
6773
6774void CEEPreloader::Error(mdToken token, Exception * pException)
6775{
6776 STANDARD_VM_CONTRACT;
6777
6778 HRESULT hr = pException->GetHR();
6779 UINT resID = 0;
6780
6781 StackSString msg;
6782
6783#ifdef CROSSGEN_COMPILE
6784 pException->GetMessage(msg);
6785
6786 // Do we have an EEException with a resID?
6787 if (EEMessageException::IsEEMessageException(pException))
6788 {
6789 EEMessageException * pEEMessageException = (EEMessageException *) pException;
6790 resID = pEEMessageException->GetResID();
6791 }
6792#else
6793 {
6794 GCX_COOP();
6795
6796 // Going though throwable gives more verbose error messages in certain cases that our tests depend on.
6797 OBJECTREF throwable = NingenEnabled() ? NULL : CLRException::GetThrowableFromException(pException);
6798
6799 if (throwable != NULL)
6800 {
6801 GetExceptionMessage(throwable, msg);
6802 }
6803 else
6804 {
6805 pException->GetMessage(msg);
6806 }
6807 }
6808#endif
6809
6810 m_pData->Error(token, hr, resID, msg.GetUnicode());
6811}
6812
6813CEEInfo *g_pCEEInfo = NULL;
6814
6815ICorDynamicInfo * __stdcall GetZapJitInfo()
6816{
6817 STANDARD_VM_CONTRACT;
6818
6819 if (g_pCEEInfo == NULL)
6820 {
6821 CEEInfo * p = new CEEInfo();
6822 if (InterlockedCompareExchangeT(&g_pCEEInfo, p, NULL) != NULL)
6823 delete p;
6824 }
6825
6826 return g_pCEEInfo;
6827}
6828
6829CEECompileInfo *g_pCEECompileInfo = NULL;
6830
6831ICorCompileInfo * __stdcall GetCompileInfo()
6832{
6833 STANDARD_VM_CONTRACT;
6834
6835 if (g_pCEECompileInfo == NULL)
6836 {
6837 CEECompileInfo * p = new CEECompileInfo();
6838 if (InterlockedCompareExchangeT(&g_pCEECompileInfo, p, NULL) != NULL)
6839 delete p;
6840 }
6841
6842 return g_pCEECompileInfo;
6843}
6844
6845//
6846// CompilationDomain
6847//
6848
6849CompilationDomain::CompilationDomain(BOOL fForceDebug,
6850 BOOL fForceProfiling,
6851 BOOL fForceInstrument)
6852 : m_fForceDebug(fForceDebug),
6853 m_fForceProfiling(fForceProfiling),
6854 m_fForceInstrument(fForceInstrument),
6855 m_pTargetAssembly(NULL),
6856 m_pTargetModule(NULL),
6857 m_pTargetImage(NULL),
6858 m_pEmit(NULL),
6859 m_pDependencyRefSpecs(NULL),
6860 m_pDependencies(NULL),
6861 m_cDependenciesCount(0),
6862 m_cDependenciesAlloc(0)
6863{
6864 STANDARD_VM_CONTRACT;
6865
6866}
6867
6868void CompilationDomain::ReleaseDependencyEmitter()
6869{
6870 m_pDependencyRefSpecs.Release();
6871
6872 m_pEmit.Release();
6873}
6874
6875CompilationDomain::~CompilationDomain()
6876{
6877 CONTRACTL
6878 {
6879 NOTHROW;
6880 GC_TRIGGERS;
6881 MODE_ANY;
6882 }
6883 CONTRACTL_END;
6884
6885 if (m_pDependencies != NULL)
6886 delete [] m_pDependencies;
6887
6888 ReleaseDependencyEmitter();
6889
6890 for (unsigned i = 0; i < m_rRefCaches.Size(); i++)
6891 {
6892 delete m_rRefCaches[i];
6893 m_rRefCaches[i]=NULL;
6894 }
6895
6896}
6897
6898void CompilationDomain::Init()
6899{
6900 STANDARD_VM_CONTRACT;
6901
6902#ifndef CROSSGEN_COMPILE
6903 AppDomain::Init();
6904#endif
6905
6906#ifndef CROSSGEN_COMPILE
6907 // allocate a Virtual Call Stub Manager for the compilation domain
6908 InitVSD();
6909#endif
6910
6911 SetCompilationDomain();
6912}
6913
6914HRESULT CompilationDomain::AddDependencyEntry(PEAssembly *pFile,
6915 mdAssemblyRef ref,
6916 mdAssemblyRef def)
6917{
6918#ifdef _DEBUG
6919 // This method is not multi-thread safe. This is OK because it is only called by NGen compiling, which is
6920 // effectively single-threaded. The following code verifies that we're not called on multiple threads.
6921 static volatile LONG threadId = 0;
6922 if (threadId == 0)
6923 {
6924 InterlockedCompareExchange(&threadId, GetCurrentThreadId(), 0);
6925 }
6926 _ASSERTE((LONG)GetCurrentThreadId() == threadId);
6927#endif // _DEBUG
6928
6929 _ASSERTE((pFile == NULL) == (def == mdAssemblyRefNil));
6930
6931 if (m_cDependenciesCount == m_cDependenciesAlloc)
6932 {
6933 // Save the new count in a local variable. Can't update m_cDependenciesAlloc until the new
6934 // CORCOMPILE_DEPENDENCY array is allocated, otherwise an out-of-memory exception from new[]
6935 // operator would put the data in an inconsistent state, causing heap corruption later.
6936 USHORT cNewDependenciesAlloc = m_cDependenciesAlloc == 0 ? 20 : m_cDependenciesAlloc * 2;
6937
6938 // Grow m_pDependencies
6939
6940 NewArrayHolder<CORCOMPILE_DEPENDENCY> pNewDependencies(new CORCOMPILE_DEPENDENCY[cNewDependenciesAlloc]);
6941 {
6942 // This block must execute transactionally. No throwing allowed. No bailing allowed.
6943 FAULT_FORBID();
6944
6945 memset(pNewDependencies, 0, cNewDependenciesAlloc*sizeof(CORCOMPILE_DEPENDENCY));
6946
6947 if (m_pDependencies)
6948 {
6949 memcpy(pNewDependencies, m_pDependencies,
6950 m_cDependenciesCount*sizeof(CORCOMPILE_DEPENDENCY));
6951
6952 delete [] m_pDependencies;
6953 }
6954
6955 m_pDependencies = pNewDependencies.Extract();
6956 m_cDependenciesAlloc = cNewDependenciesAlloc;
6957 }
6958 }
6959
6960 CORCOMPILE_DEPENDENCY *pDependency = &m_pDependencies[m_cDependenciesCount++];
6961
6962 // Clear memory so that we won't write random data into the zapped file
6963 ZeroMemory(pDependency, sizeof(CORCOMPILE_DEPENDENCY));
6964
6965 pDependency->dwAssemblyRef = ref;
6966
6967 pDependency->dwAssemblyDef = def;
6968
6969 pDependency->signNativeImage = INVALID_NGEN_SIGNATURE;
6970
6971 if (pFile)
6972 {
6973 DomainAssembly *pAssembly = GetAppDomain()->LoadDomainAssembly(NULL, pFile, FILE_LOAD_CREATE);
6974 // Note that this can trigger an assembly load (of mscorlib)
6975 pAssembly->GetOptimizedIdentitySignature(&pDependency->signAssemblyDef);
6976
6977
6978
6979 //
6980 // This is done in CompilationDomain::CanEagerBindToZapFile with full support for hardbinding
6981 //
6982 if (pFile->IsSystem() && pFile->HasNativeImage())
6983 {
6984 CORCOMPILE_VERSION_INFO * pNativeVersion = pFile->GetLoadedNative()->GetNativeVersionInfo();
6985 pDependency->signNativeImage = pNativeVersion->signature;
6986 }
6987
6988 }
6989
6990 return S_OK;
6991}
6992
6993HRESULT CompilationDomain::AddDependency(AssemblySpec *pRefSpec,
6994 PEAssembly * pFile)
6995{
6996 HRESULT hr;
6997
6998 //
6999 // Record the dependency
7000 //
7001
7002 // This assert prevents dependencies from silently being loaded without being recorded.
7003 _ASSERTE(m_pEmit);
7004
7005 // Normalize any reference to mscorlib; we don't want to record other non-canonical
7006 // mscorlib references in the ngen image since fusion doesn't understand how to bind them.
7007 // (Not to mention the fact that they are redundant.)
7008 AssemblySpec spec;
7009 if (pRefSpec->IsMscorlib())
7010 {
7011 _ASSERTE(pFile); // mscorlib had better not be missing
7012 if (!pFile)
7013 return E_UNEXPECTED;
7014
7015 // Don't store a binding from mscorlib to itself.
7016 if (m_pTargetAssembly == SystemDomain::SystemAssembly())
7017 return S_OK;
7018
7019 spec.InitializeSpec(pFile);
7020 pRefSpec = &spec;
7021 }
7022 else if (m_pTargetAssembly == NULL && pFile)
7023 {
7024 // If target assembly is still NULL, we must be loading either the target assembly or mscorlib.
7025 // Mscorlib is already handled above, so we must be loading the target assembly if we get here.
7026 // Use the assembly name given in the target assembly so that the native image is deterministic
7027 // regardless of how the target assembly is specified on the command line.
7028 spec.InitializeSpec(pFile);
7029 if (spec.IsStrongNamed() && spec.HasPublicKey())
7030 {
7031 spec.ConvertPublicKeyToToken();
7032 }
7033 pRefSpec = &spec;
7034 }
7035 else if (pRefSpec->IsStrongNamed() && pRefSpec->HasPublicKey())
7036 {
7037 // Normalize to always use public key token. Otherwise we may insert one reference
7038 // using public key, and another reference using public key token.
7039 spec.CopyFrom(pRefSpec);
7040 spec.ConvertPublicKeyToToken();
7041 pRefSpec = &spec;
7042 }
7043
7044#ifdef FEATURE_COMINTEROP
7045 // Only cache ref specs that have a unique identity. This is needed to avoid caching
7046 // things like WinRT type specs, which would benefit very little from being cached.
7047 if (!pRefSpec->HasUniqueIdentity())
7048 {
7049 // Successful bind of a reference with a non-unique assembly identity.
7050 _ASSERTE(pRefSpec->IsContentType_WindowsRuntime());
7051
7052 AssemblySpec defSpec;
7053 if (pFile != NULL)
7054 {
7055 defSpec.InitializeSpec(pFile);
7056
7057 // Windows Runtime Native Image binding depends on details exclusively described by the definition winmd file.
7058 // Therefore we can actually drop the existing ref spec here entirely.
7059 // Also, Windows Runtime Native Image binding uses the simple name of the ref spec as the
7060 // resolution rule for PreBind when finding definition assemblies.
7061 // See comment on CLRPrivBinderWinRT::PreBind for further details.
7062 pRefSpec = &defSpec;
7063 }
7064
7065 // Unfortunately, we don't have any choice regarding failures (pFile == NULL) because
7066 // there is no value to canonicalize on (i.e., a def spec created from a non-NULL
7067 // pFile) and so we must cache all non-unique-assembly-id failures.
7068 const AssemblySpecDefRefMapEntry * pEntry = m_dependencyDefRefMap.LookupPtr(&defSpec);
7069 if (pFile == NULL || pEntry == NULL)
7070 {
7071 mdAssemblyRef refToken = mdAssemblyRefNil;
7072 IfFailRet(pRefSpec->EmitToken(m_pEmit, &refToken, TRUE, TRUE));
7073
7074 mdAssemblyRef defToken = mdAssemblyRefNil;
7075 if (pFile != NULL)
7076 {
7077 IfFailRet(defSpec.EmitToken(m_pEmit, &defToken, TRUE, TRUE));
7078
7079 NewHolder<AssemblySpec> pNewDefSpec = new AssemblySpec();
7080 pNewDefSpec->CopyFrom(&defSpec);
7081 pNewDefSpec->CloneFields();
7082
7083 NewHolder<AssemblySpec> pNewRefSpec = new AssemblySpec();
7084 pNewRefSpec->CopyFrom(pRefSpec);
7085 pNewRefSpec->CloneFields();
7086
7087 _ASSERTE(m_dependencyDefRefMap.LookupPtr(pNewDefSpec) == NULL);
7088
7089 AssemblySpecDefRefMapEntry e;
7090 e.m_pDef = pNewDefSpec;
7091 e.m_pRef = pNewRefSpec;
7092 m_dependencyDefRefMap.Add(e);
7093
7094 pNewDefSpec.SuppressRelease();
7095 pNewRefSpec.SuppressRelease();
7096 }
7097
7098 IfFailRet(AddDependencyEntry(pFile, refToken, defToken));
7099 }
7100 }
7101 else
7102#endif // FEATURE_COMINTEROP
7103 {
7104 //
7105 // See if we've already added the contents of the ref
7106 // Else, emit token for the ref
7107 //
7108
7109 if (m_pDependencyRefSpecs->Store(pRefSpec))
7110 return S_OK;
7111
7112 mdAssemblyRef refToken;
7113 IfFailRet(pRefSpec->EmitToken(m_pEmit, &refToken));
7114
7115 //
7116 // Make a spec for the bound assembly
7117 //
7118
7119 mdAssemblyRef defToken = mdAssemblyRefNil;
7120
7121 // All dependencies of a shared assembly need to be shared. So for a shared
7122 // assembly, we want to remember the missing assembly ref during ngen, so that
7123 // we can probe eagerly for the dependency at load time, and make sure that
7124 // it is loaded as shared.
7125 // In such a case, pFile will be NULL
7126 if (pFile)
7127 {
7128 AssemblySpec assemblySpec;
7129 assemblySpec.InitializeSpec(pFile);
7130
7131 IfFailRet(assemblySpec.EmitToken(m_pEmit, &defToken));
7132 }
7133
7134 //
7135 // Add the entry. Include the PEFile if we are not doing explicit bindings.
7136 //
7137
7138 IfFailRet(AddDependencyEntry(pFile, refToken, defToken));
7139 }
7140
7141 return S_OK;
7142}
7143
7144//----------------------------------------------------------------------------
7145AssemblySpec* CompilationDomain::FindAssemblyRefSpecForDefSpec(
7146 AssemblySpec* pDefSpec)
7147{
7148 WRAPPER_NO_CONTRACT;
7149
7150 if (pDefSpec == nullptr)
7151 return nullptr;
7152
7153 const AssemblySpecDefRefMapEntry * pEntry = m_dependencyDefRefMap.LookupPtr(pDefSpec);
7154 _ASSERTE(pEntry != NULL);
7155
7156 return (pEntry != NULL) ? pEntry->m_pRef : NULL;
7157}
7158
7159
7160//----------------------------------------------------------------------------
7161// Is it OK to embed direct pointers to an ngen dependency?
7162// true if hardbinding is OK, false otherwise
7163//
7164// targetModule - The pointer points into the native image of this Module.
7165// If this native image gets relocated, the native image of
7166// the source Module is invalidated unless the embedded
7167// pointer can be fixed up appropriately.
7168// limitToHardBindList - Is it OK to hard-bind to a dependency even if it is
7169// not asked for explicitly?
7170
7171BOOL CompilationDomain::CanEagerBindToZapFile(Module *targetModule, BOOL limitToHardBindList)
7172{
7173 // We do this check before checking the hashtables because m_cantHardBindModules
7174 // will contain non-manifest modules. However, we do want them to be able
7175 // to hard-bind to themselves
7176 if (targetModule == m_pTargetModule)
7177 {
7178 return TRUE;
7179 }
7180
7181 //
7182 // CoreCLR does not have attributes for fine grained eager binding control.
7183 // We hard bind to mscorlib.dll only.
7184 //
7185 return targetModule->IsSystem();
7186}
7187
7188
7189void CompilationDomain::SetTarget(Assembly *pAssembly, Module *pModule)
7190{
7191 STANDARD_VM_CONTRACT;
7192
7193 m_pTargetAssembly = pAssembly;
7194 m_pTargetModule = pModule;
7195}
7196
7197void CompilationDomain::SetTargetImage(DataImage *pImage, CEEPreloader * pPreloader)
7198{
7199 STANDARD_VM_CONTRACT;
7200
7201 m_pTargetImage = pImage;
7202 m_pTargetPreloader = pPreloader;
7203
7204 _ASSERTE(pImage->GetModule() == GetTargetModule());
7205}
7206
7207void ReportMissingDependency(Exception * e)
7208{
7209 // Avoid duplicate error messages
7210 if (FAILED(g_hrFatalError))
7211 return;
7212
7213 SString s;
7214
7215 e->GetMessage(s);
7216 GetSvcLogger()->Printf(LogLevel_Error, W("Error: %s\n"), s.GetUnicode());
7217
7218 g_hrFatalError = COR_E_FILELOAD;
7219}
7220
7221PEAssembly *CompilationDomain::BindAssemblySpec(
7222 AssemblySpec *pSpec,
7223 BOOL fThrowOnFileNotFound,
7224 BOOL fUseHostBinderIfAvailable)
7225{
7226 PEAssembly *pFile = NULL;
7227 //
7228 // Do the binding
7229 //
7230
7231 EX_TRY
7232 {
7233 //
7234 // Use normal binding rules
7235 // (possibly with our custom IApplicationContext)
7236 //
7237 pFile = AppDomain::BindAssemblySpec(
7238 pSpec,
7239 fThrowOnFileNotFound,
7240 fUseHostBinderIfAvailable);
7241 }
7242 EX_HOOK
7243 {
7244 if (!g_fNGenMissingDependenciesOk)
7245 {
7246 ReportMissingDependency(GET_EXCEPTION());
7247 EX_RETHROW;
7248 }
7249
7250 //
7251 // Record missing dependencies
7252 //
7253#ifdef FEATURE_COMINTEROP
7254 if (!g_fNGenWinMDResilient || pSpec->HasUniqueIdentity())
7255#endif
7256 {
7257 IfFailThrow(AddDependency(pSpec, NULL));
7258 }
7259 }
7260 EX_END_HOOK
7261
7262#ifdef FEATURE_COMINTEROP
7263 if (!g_fNGenWinMDResilient || pSpec->HasUniqueIdentity())
7264#endif
7265 {
7266 IfFailThrow(AddDependency(pSpec, pFile));
7267 }
7268
7269 return pFile;
7270}
7271
7272HRESULT
7273 CompilationDomain::SetContextInfo(LPCWSTR path, BOOL isExe)
7274{
7275 STANDARD_VM_CONTRACT;
7276
7277 HRESULT hr = S_OK;
7278
7279 COOPERATIVE_TRANSITION_BEGIN();
7280
7281
7282 COOPERATIVE_TRANSITION_END();
7283
7284 return hr;
7285}
7286
7287void CompilationDomain::SetDependencyEmitter(IMetaDataAssemblyEmit *pEmit)
7288{
7289 STANDARD_VM_CONTRACT;
7290
7291 pEmit->AddRef();
7292 m_pEmit = pEmit;
7293
7294 m_pDependencyRefSpecs = new AssemblySpecHash();
7295}
7296
7297
7298HRESULT
7299 CompilationDomain::GetDependencies(CORCOMPILE_DEPENDENCY **ppDependencies,
7300 DWORD *pcDependencies)
7301{
7302 STANDARD_VM_CONTRACT;
7303
7304
7305 //
7306 // Return the bindings.
7307 //
7308
7309 *ppDependencies = m_pDependencies;
7310 *pcDependencies = m_cDependenciesCount;
7311
7312 // Cannot add any more dependencies
7313 ReleaseDependencyEmitter();
7314
7315 return S_OK;
7316}
7317
7318
7319#ifdef CROSSGEN_COMPILE
7320HRESULT CompilationDomain::SetPlatformWinmdPaths(LPCWSTR pwzPlatformWinmdPaths)
7321{
7322 STANDARD_VM_CONTRACT;
7323
7324#ifdef FEATURE_COMINTEROP
7325 // Create the array list on the heap since it will be passed off for the Crossgen RoResolveNamespace mockup to keep for the life of the process
7326 StringArrayList *saPaths = new StringArrayList();
7327
7328 SString strPaths(pwzPlatformWinmdPaths);
7329 if (!strPaths.IsEmpty())
7330 {
7331 for (SString::Iterator i = strPaths.Begin(); i != strPaths.End(); )
7332 {
7333 // Skip any leading spaces or semicolons
7334 if (strPaths.Skip(i, W(';')))
7335 {
7336 continue;
7337 }
7338
7339 SString::Iterator iEnd = i; // Where current assembly name ends
7340 SString::Iterator iNext; // Where next assembly name starts
7341 if (strPaths.Find(iEnd, W(';')))
7342 {
7343 iNext = iEnd + 1;
7344 }
7345 else
7346 {
7347 iNext = iEnd = strPaths.End();
7348 }
7349
7350 _ASSERTE(i < iEnd);
7351 if(i != iEnd)
7352 {
7353 saPaths->Append(SString(strPaths, i, iEnd));
7354 }
7355 i = iNext;
7356 }
7357 }
7358 Crossgen::SetFirstPartyWinMDPaths(saPaths);
7359#endif // FEATURE_COMINTEROP
7360
7361 return S_OK;
7362}
7363#endif // CROSSGEN_COMPILE
7364
7365
7366#endif // FEATURE_PREJIT
7367