| 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: Field.cpp |
| 6 | // |
| 7 | |
| 8 | // =========================================================================== |
| 9 | // This file contains the implementation for FieldDesc methods. |
| 10 | // =========================================================================== |
| 11 | // |
| 12 | |
| 13 | |
| 14 | #include "common.h" |
| 15 | |
| 16 | #include "encee.h" |
| 17 | #include "field.h" |
| 18 | #include "generics.h" |
| 19 | |
| 20 | #include "peimagelayout.inl" |
| 21 | |
| 22 | // called from code:MethodTableBuilder::InitializeFieldDescs#InitCall |
| 23 | VOID FieldDesc::Init(mdFieldDef mb, CorElementType FieldType, DWORD dwMemberAttrs, BOOL fIsStatic, BOOL fIsRVA, BOOL fIsThreadLocal, LPCSTR pszFieldName) |
| 24 | { |
| 25 | LIMITED_METHOD_CONTRACT; |
| 26 | |
| 27 | // We allow only a subset of field types here - all objects must be set to TYPE_CLASS |
| 28 | // By-value classes are ELEMENT_TYPE_VALUETYPE |
| 29 | _ASSERTE( |
| 30 | FieldType == ELEMENT_TYPE_I1 || |
| 31 | FieldType == ELEMENT_TYPE_BOOLEAN || |
| 32 | FieldType == ELEMENT_TYPE_U1 || |
| 33 | FieldType == ELEMENT_TYPE_I2 || |
| 34 | FieldType == ELEMENT_TYPE_U2 || |
| 35 | FieldType == ELEMENT_TYPE_CHAR || |
| 36 | FieldType == ELEMENT_TYPE_I4 || |
| 37 | FieldType == ELEMENT_TYPE_U4 || |
| 38 | FieldType == ELEMENT_TYPE_I8 || |
| 39 | FieldType == ELEMENT_TYPE_I || |
| 40 | FieldType == ELEMENT_TYPE_U || |
| 41 | FieldType == ELEMENT_TYPE_U8 || |
| 42 | FieldType == ELEMENT_TYPE_R4 || |
| 43 | FieldType == ELEMENT_TYPE_R8 || |
| 44 | FieldType == ELEMENT_TYPE_CLASS || |
| 45 | FieldType == ELEMENT_TYPE_VALUETYPE || |
| 46 | FieldType == ELEMENT_TYPE_PTR || |
| 47 | FieldType == ELEMENT_TYPE_FNPTR |
| 48 | ); |
| 49 | _ASSERTE(fIsStatic || (!fIsRVA && !fIsThreadLocal)); |
| 50 | _ASSERTE(fIsRVA + fIsThreadLocal <= 1); |
| 51 | |
| 52 | m_requiresFullMbValue = 0; |
| 53 | SetMemberDef(mb); |
| 54 | |
| 55 | m_type = FieldType; |
| 56 | m_prot = fdFieldAccessMask & dwMemberAttrs; |
| 57 | m_isStatic = fIsStatic != 0; |
| 58 | m_isRVA = fIsRVA != 0; |
| 59 | m_isThreadLocal = fIsThreadLocal != 0; |
| 60 | |
| 61 | #ifdef _DEBUG |
| 62 | m_debugName = (LPUTF8)pszFieldName; |
| 63 | #endif |
| 64 | |
| 65 | _ASSERTE(GetMemberDef() == mb); // no truncation |
| 66 | _ASSERTE(GetFieldType() == FieldType); |
| 67 | _ASSERTE(GetFieldProtection() == (fdFieldAccessMask & dwMemberAttrs)); |
| 68 | _ASSERTE((BOOL) IsStatic() == (fIsStatic != 0)); |
| 69 | } |
| 70 | |
| 71 | // Return whether the field is a GC ref type |
| 72 | BOOL FieldDesc::IsObjRef() |
| 73 | { |
| 74 | WRAPPER_NO_CONTRACT; |
| 75 | SUPPORTS_DAC; |
| 76 | return CorTypeInfo::IsObjRef_NoThrow(GetFieldType()); |
| 77 | } |
| 78 | |
| 79 | #ifndef DACCESS_COMPILE |
| 80 | void FieldDesc::PrecomputeNameHash() |
| 81 | { |
| 82 | CONTRACTL |
| 83 | { |
| 84 | STANDARD_VM_CHECK; |
| 85 | PRECONDITION(IsCompilationProcess()); |
| 86 | } |
| 87 | CONTRACTL_END; |
| 88 | |
| 89 | // We only have space for the name hash when we can use the packed mb layout |
| 90 | if (m_requiresFullMbValue) |
| 91 | { |
| 92 | return; |
| 93 | } |
| 94 | |
| 95 | // Store a case-insensitive hash so that we can use this value for |
| 96 | // both case-sensitive and case-insensitive name lookups |
| 97 | SString name(SString::Utf8Literal, GetName()); |
| 98 | ULONG nameHashValue = name.HashCaseInsensitive() & enum_packedMbLayout_NameHashMask; |
| 99 | |
| 100 | // We should never overwrite any other bits |
| 101 | _ASSERTE((m_mb & enum_packedMbLayout_NameHashMask) == 0 || |
| 102 | (m_mb & enum_packedMbLayout_NameHashMask) == nameHashValue); |
| 103 | |
| 104 | m_mb |= nameHashValue; |
| 105 | } |
| 106 | #endif |
| 107 | |
| 108 | BOOL FieldDesc::MightHaveName(ULONG nameHashValue) |
| 109 | { |
| 110 | LIMITED_METHOD_CONTRACT; |
| 111 | |
| 112 | g_IBCLogger.LogFieldDescsAccess(this); |
| 113 | |
| 114 | // We only have space for a name hash when we are using the packed mb layout |
| 115 | if (m_requiresFullMbValue) |
| 116 | { |
| 117 | return TRUE; |
| 118 | } |
| 119 | |
| 120 | ULONG thisHashValue = m_mb & enum_packedMbLayout_NameHashMask; |
| 121 | |
| 122 | // A zero value might mean no hash has ever been set |
| 123 | // (checking this way is better than dedicating a bit to tell us) |
| 124 | if (thisHashValue == 0) |
| 125 | { |
| 126 | return TRUE; |
| 127 | } |
| 128 | |
| 129 | ULONG testHashValue = nameHashValue & enum_packedMbLayout_NameHashMask; |
| 130 | |
| 131 | return (thisHashValue == testHashValue); |
| 132 | } |
| 133 | |
| 134 | #ifndef DACCESS_COMPILE //we don't require DAC to special case simple types |
| 135 | // Return the type of the field, as a class, but only if it's loaded. |
| 136 | TypeHandle FieldDesc::LookupFieldTypeHandle(ClassLoadLevel level, BOOL dropGenericArgumentLevel) |
| 137 | { |
| 138 | |
| 139 | CONTRACTL |
| 140 | { |
| 141 | INSTANCE_CHECK; |
| 142 | NOTHROW; |
| 143 | GC_NOTRIGGER; |
| 144 | MODE_ANY; |
| 145 | FORBID_FAULT; |
| 146 | SO_TOLERANT; |
| 147 | } |
| 148 | CONTRACTL_END |
| 149 | |
| 150 | // This function is called during GC promotion. |
| 151 | ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); |
| 152 | |
| 153 | // Caller should have handled all the non-class cases, already. |
| 154 | _ASSERTE(GetFieldType() == ELEMENT_TYPE_CLASS || |
| 155 | GetFieldType() == ELEMENT_TYPE_VALUETYPE); |
| 156 | |
| 157 | MetaSig sig(this); |
| 158 | CorElementType type; |
| 159 | |
| 160 | type = sig.NextArg(); |
| 161 | |
| 162 | // This may be the real type which includes other things |
| 163 | // beside class and value class such as arrays |
| 164 | _ASSERTE(type == ELEMENT_TYPE_CLASS || |
| 165 | type == ELEMENT_TYPE_VALUETYPE || |
| 166 | type == ELEMENT_TYPE_STRING || |
| 167 | type == ELEMENT_TYPE_SZARRAY || |
| 168 | type == ELEMENT_TYPE_VAR |
| 169 | ); |
| 170 | |
| 171 | // == FailIfNotLoaded, can also assert that the thing is restored |
| 172 | TypeHandle th = NULL; |
| 173 | |
| 174 | BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return NULL); |
| 175 | { |
| 176 | th = sig.GetLastTypeHandleThrowing(ClassLoader::DontLoadTypes, level, dropGenericArgumentLevel); |
| 177 | } |
| 178 | END_SO_INTOLERANT_CODE; |
| 179 | |
| 180 | return th; |
| 181 | } |
| 182 | #else //simplified version |
| 183 | TypeHandle FieldDesc::LookupFieldTypeHandle(ClassLoadLevel level, BOOL dropGenericArgumentLevel) |
| 184 | { |
| 185 | WRAPPER_NO_CONTRACT; |
| 186 | MetaSig sig(this); |
| 187 | CorElementType type; |
| 188 | type = sig.NextArg(); |
| 189 | return sig.GetLastTypeHandleThrowing(ClassLoader::DontLoadTypes, level, dropGenericArgumentLevel); |
| 190 | } |
| 191 | #endif //DACCESS_COMPILE |
| 192 | |
| 193 | TypeHandle FieldDesc::GetFieldTypeHandleThrowing(ClassLoadLevel level/*=CLASS_LOADED*/, |
| 194 | BOOL dropGenericArgumentLevel /*=FALSE*/) |
| 195 | { |
| 196 | WRAPPER_NO_CONTRACT; |
| 197 | |
| 198 | MetaSig sig(this); |
| 199 | sig.NextArg(); |
| 200 | |
| 201 | return sig.GetLastTypeHandleThrowing(ClassLoader::LoadTypes, level, dropGenericArgumentLevel); |
| 202 | } |
| 203 | |
| 204 | #ifndef DACCESS_COMPILE |
| 205 | |
| 206 | void* FieldDesc::GetStaticAddress(void *base) |
| 207 | { |
| 208 | CONTRACTL |
| 209 | { |
| 210 | NOTHROW; |
| 211 | GC_NOTRIGGER; |
| 212 | SO_TOLERANT; |
| 213 | MODE_ANY; // Needed by profiler and server GC |
| 214 | } |
| 215 | CONTRACTL_END; |
| 216 | |
| 217 | void* ret = GetStaticAddressHandle(base); // Get the handle |
| 218 | |
| 219 | // For value classes, the handle points at an OBJECTREF |
| 220 | // which holds the boxed value class, so derefernce and unbox. |
| 221 | if (GetFieldType() == ELEMENT_TYPE_VALUETYPE && !IsRVA()) |
| 222 | { |
| 223 | OBJECTREF obj = ObjectToOBJECTREF(*(Object**) ret); |
| 224 | ret = obj->GetData(); |
| 225 | } |
| 226 | return ret; |
| 227 | } |
| 228 | |
| 229 | MethodTable * FieldDesc::GetExactDeclaringType(MethodTable * ownerOrSubType) |
| 230 | { |
| 231 | CONTRACTL |
| 232 | { |
| 233 | NOTHROW; |
| 234 | GC_NOTRIGGER; |
| 235 | SO_TOLERANT; |
| 236 | MODE_ANY; |
| 237 | } |
| 238 | CONTRACTL_END; |
| 239 | |
| 240 | MethodTable * pMT = GetApproxEnclosingMethodTable(); |
| 241 | |
| 242 | // Fast path for typical case. |
| 243 | if (ownerOrSubType == pMT) |
| 244 | return pMT; |
| 245 | |
| 246 | return ownerOrSubType->GetMethodTableMatchingParentClass(pMT); |
| 247 | } |
| 248 | |
| 249 | #endif // #ifndef DACCESS_COMPILE |
| 250 | |
| 251 | // static value classes are actually stored in their boxed form. |
| 252 | // this means that their address moves. |
| 253 | PTR_VOID FieldDesc::GetStaticAddressHandle(PTR_VOID base) |
| 254 | { |
| 255 | |
| 256 | CONTRACTL |
| 257 | { |
| 258 | INSTANCE_CHECK; |
| 259 | NOTHROW; |
| 260 | GC_NOTRIGGER; |
| 261 | MODE_ANY; |
| 262 | FORBID_FAULT; |
| 263 | SO_TOLERANT; |
| 264 | PRECONDITION(IsStatic()); |
| 265 | PRECONDITION(GetEnclosingMethodTable()->IsRestored_NoLogging()); |
| 266 | } |
| 267 | CONTRACTL_END |
| 268 | |
| 269 | g_IBCLogger.LogFieldDescsAccess(this); |
| 270 | |
| 271 | _ASSERTE(IsStatic()); |
| 272 | #ifdef EnC_SUPPORTED |
| 273 | if (IsEnCNew()) |
| 274 | { |
| 275 | EnCFieldDesc * pFD = dac_cast<PTR_EnCFieldDesc>(this); |
| 276 | _ASSERTE_IMPL(pFD->GetApproxEnclosingMethodTable()->SanityCheck()); |
| 277 | _ASSERTE(pFD->GetModule()->IsEditAndContinueEnabled()); |
| 278 | |
| 279 | EditAndContinueModule *pModule = (EditAndContinueModule*)pFD->GetModule(); |
| 280 | _ASSERTE(pModule->IsEditAndContinueEnabled()); |
| 281 | |
| 282 | PTR_VOID retVal = NULL; |
| 283 | |
| 284 | // BEGIN_SO_INTOLERANT_CODE will throw if we don't have enough stack |
| 285 | // and GetStaticAddressHandle has no failure semantics, so we need |
| 286 | // to just do the SO policy (e.g. rip the appdomain or process). |
| 287 | CONTRACT_VIOLATION(ThrowsViolation) |
| 288 | |
| 289 | #ifdef DACCESS_COMPILE |
| 290 | DacNotImpl(); |
| 291 | #else |
| 292 | BEGIN_SO_INTOLERANT_CODE(GetThread()); |
| 293 | { |
| 294 | GCX_COOP(); |
| 295 | // This routine doesn't have a failure semantic - but Resolve*Field(...) does. |
| 296 | // Something needs to be rethought here and I think it's E&C. |
| 297 | CONTRACT_VIOLATION(ThrowsViolation|FaultViolation|GCViolation); //B#25680 (Fix Enc violations) |
| 298 | retVal = (void *)(pModule->ResolveOrAllocateField(NULL, pFD)); |
| 299 | } |
| 300 | END_SO_INTOLERANT_CODE; |
| 301 | #endif // !DACCESS_COMPILE |
| 302 | return retVal; |
| 303 | } |
| 304 | #endif // EnC_SUPPORTED |
| 305 | |
| 306 | |
| 307 | if (IsRVA()) |
| 308 | { |
| 309 | Module* pModule = GetModule(); |
| 310 | PTR_VOID ret = pModule->GetRvaField(GetOffset(), IsZapped()); |
| 311 | |
| 312 | _ASSERTE(!pModule->IsPEFile() || !pModule->IsRvaFieldTls(GetOffset())); |
| 313 | |
| 314 | return(ret); |
| 315 | } |
| 316 | |
| 317 | CONSISTENCY_CHECK(CheckPointer(base)); |
| 318 | |
| 319 | PTR_VOID ret = PTR_VOID(dac_cast<PTR_BYTE>(base) + GetOffset()); |
| 320 | |
| 321 | |
| 322 | return ret; |
| 323 | } |
| 324 | |
| 325 | |
| 326 | #ifndef CROSSGEN_COMPILE |
| 327 | |
| 328 | // These routines encapsulate the operation of getting and setting |
| 329 | // fields. |
| 330 | void FieldDesc::GetInstanceField(OBJECTREF o, VOID * pOutVal) |
| 331 | { |
| 332 | CONTRACTL |
| 333 | { |
| 334 | INSTANCE_CHECK; |
| 335 | if (FORBIDGC_LOADER_USE_ENABLED() ) NOTHROW; else THROWS; |
| 336 | if (FORBIDGC_LOADER_USE_ENABLED() ) GC_NOTRIGGER; else GC_TRIGGERS; |
| 337 | MODE_ANY; |
| 338 | if (FORBIDGC_LOADER_USE_ENABLED() ) FORBID_FAULT; else INJECT_FAULT(COMPlusThrowOM();); |
| 339 | } |
| 340 | CONTRACTL_END |
| 341 | |
| 342 | // We know that it isn't going to be null here. Tell PREFIX that we know it. |
| 343 | PREFIX_ASSUME(o != NULL); |
| 344 | |
| 345 | // Check whether we are getting a field value on a proxy. If so, then ask |
| 346 | // remoting services to extract the value from the instance. |
| 347 | |
| 348 | // Unbox the value class |
| 349 | TADDR pFieldAddress = (TADDR)GetInstanceAddress(o); |
| 350 | UINT cbSize = GetSize(); |
| 351 | |
| 352 | switch (cbSize) |
| 353 | { |
| 354 | case 1: |
| 355 | *(INT8*)pOutVal = VolatileLoad<INT8>(PTR_INT8(pFieldAddress)); |
| 356 | break; |
| 357 | |
| 358 | case 2: |
| 359 | *(INT16*)pOutVal = VolatileLoad<INT16>(PTR_INT16(pFieldAddress)); |
| 360 | break; |
| 361 | |
| 362 | case 4: |
| 363 | *(INT32*)pOutVal = VolatileLoad<INT32>(PTR_INT32(pFieldAddress)); |
| 364 | break; |
| 365 | |
| 366 | case 8: |
| 367 | *(INT64*)pOutVal = VolatileLoad<INT64>(PTR_INT64(pFieldAddress)); |
| 368 | break; |
| 369 | |
| 370 | default: |
| 371 | UNREACHABLE(); |
| 372 | break; |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | #ifndef DACCESS_COMPILE |
| 377 | void FieldDesc::SetInstanceField(OBJECTREF o, const VOID * pInVal) |
| 378 | { |
| 379 | CONTRACTL |
| 380 | { |
| 381 | INSTANCE_CHECK; |
| 382 | THROWS; |
| 383 | GC_TRIGGERS; |
| 384 | MODE_ANY; |
| 385 | INJECT_FAULT(COMPlusThrowOM();); |
| 386 | } |
| 387 | CONTRACTL_END |
| 388 | |
| 389 | #ifdef _DEBUG |
| 390 | // |
| 391 | // assert that o is derived from MT of enclosing class |
| 392 | // |
| 393 | // walk up o's inheritence chain to make sure m_pMTOfEnclosingClass is along it |
| 394 | // |
| 395 | MethodTable* pCursor = o->GetMethodTable(); |
| 396 | |
| 397 | //<TODO>@todo : work out exactly why instantiations aren't matching; probably |
| 398 | // because of approx loads on field types in the loader</TODO> |
| 399 | while (pCursor && (GetApproxEnclosingMethodTable()->HasSameTypeDefAs(pCursor))) |
| 400 | { |
| 401 | pCursor = pCursor->GetParentMethodTable(); |
| 402 | } |
| 403 | _ASSERTE(pCursor != NULL); |
| 404 | #endif // _DEBUG |
| 405 | |
| 406 | // Unbox the value class |
| 407 | LPVOID pFieldAddress = GetInstanceAddress(o); |
| 408 | |
| 409 | CorElementType fieldType = GetFieldType(); |
| 410 | |
| 411 | if (fieldType == ELEMENT_TYPE_CLASS) |
| 412 | { |
| 413 | OBJECTREF ref = ObjectToOBJECTREF(*(Object**)pInVal); |
| 414 | |
| 415 | SetObjectReference((OBJECTREF*)pFieldAddress, ref, |
| 416 | o->GetAppDomain()); |
| 417 | } |
| 418 | else if (fieldType == ELEMENT_TYPE_VALUETYPE) |
| 419 | { |
| 420 | CONSISTENCY_CHECK(!LookupFieldTypeHandle().IsNull()); |
| 421 | // The Approximate MT is enough to do the copy |
| 422 | CopyValueClass(pFieldAddress, |
| 423 | (void*)pInVal, |
| 424 | LookupFieldTypeHandle().GetMethodTable(), |
| 425 | o->GetAppDomain()); |
| 426 | } |
| 427 | else |
| 428 | { |
| 429 | UINT cbSize = LoadSize(); |
| 430 | |
| 431 | switch (cbSize) |
| 432 | { |
| 433 | case 1: |
| 434 | VolatileStore<INT8>((INT8*)pFieldAddress, *(INT8*)pInVal); |
| 435 | break; |
| 436 | |
| 437 | case 2: |
| 438 | VolatileStore<INT16>((INT16*)pFieldAddress, *(INT16*)pInVal); |
| 439 | break; |
| 440 | |
| 441 | case 4: |
| 442 | VolatileStore<INT32>((INT32*)pFieldAddress, *(INT32*)pInVal); |
| 443 | break; |
| 444 | |
| 445 | case 8: |
| 446 | VolatileStore<INT64>((INT64*)pFieldAddress, *(INT64*)pInVal); |
| 447 | break; |
| 448 | |
| 449 | default: |
| 450 | UNREACHABLE(); |
| 451 | break; |
| 452 | } |
| 453 | } |
| 454 | } |
| 455 | #endif // #ifndef DACCESS_COMPILE |
| 456 | |
| 457 | |
| 458 | // This function is used for BYREF support of fields. Since it generates |
| 459 | // interior pointers, you really have to watch the lifetime of the pointer |
| 460 | // so that GCs don't happen while you have the reference active |
| 461 | PTR_VOID FieldDesc::GetAddressNoThrowNoGC(PTR_VOID o) |
| 462 | { |
| 463 | CONTRACTL |
| 464 | { |
| 465 | NOTHROW; |
| 466 | GC_NOTRIGGER; |
| 467 | MODE_COOPERATIVE; |
| 468 | SO_TOLERANT; |
| 469 | PRECONDITION(!IsEnCNew()); |
| 470 | SO_TOLERANT; |
| 471 | SUPPORTS_DAC; |
| 472 | } |
| 473 | CONTRACTL_END; |
| 474 | |
| 475 | DWORD dwOffset = GetOffset(); |
| 476 | if (!IsFieldOfValueType()) |
| 477 | { |
| 478 | dwOffset += sizeof(Object); |
| 479 | } |
| 480 | return dac_cast<PTR_BYTE>(o) + dwOffset; |
| 481 | } |
| 482 | |
| 483 | PTR_VOID FieldDesc::GetAddress(PTR_VOID o) |
| 484 | { |
| 485 | CONTRACTL |
| 486 | { |
| 487 | if(IsEnCNew()) {THROWS;} else {DISABLED(THROWS);}; |
| 488 | if(IsEnCNew()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}; |
| 489 | SUPPORTS_DAC; |
| 490 | } |
| 491 | CONTRACTL_END; |
| 492 | |
| 493 | #ifdef DACCESS_COMPILE |
| 494 | _ASSERTE(!IsEnCNew()); // when we call this while finding an EnC field via the DAC, |
| 495 | // the field desc is for the EnCHelper, not the new EnC field |
| 496 | #endif |
| 497 | g_IBCLogger.LogFieldDescsAccess(this); |
| 498 | |
| 499 | #if defined(EnC_SUPPORTED) && !defined(DACCESS_COMPILE) |
| 500 | // EnC added fields aren't at a simple offset like normal fields. |
| 501 | if (IsEnCNew()) |
| 502 | { |
| 503 | // We'll have to go through some effort to compute the address of this field. |
| 504 | return ((EnCFieldDesc *)this)->GetAddress(o); |
| 505 | } |
| 506 | #endif // defined(EnC_SUPPORTED) && !defined(DACCESS_COMPILE) |
| 507 | return GetAddressNoThrowNoGC(o); |
| 508 | } |
| 509 | |
| 510 | void *FieldDesc::GetInstanceAddress(OBJECTREF o) |
| 511 | { |
| 512 | CONTRACTL |
| 513 | { |
| 514 | if(IsEnCNew()) {THROWS;} else {DISABLED(THROWS);}; |
| 515 | if(IsEnCNew()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);}; |
| 516 | } |
| 517 | CONTRACTL_END; |
| 518 | |
| 519 | g_IBCLogger.LogFieldDescsAccess(this); |
| 520 | |
| 521 | DWORD dwOffset = m_dwOffset; // GetOffset() |
| 522 | |
| 523 | #ifdef EnC_SUPPORTED |
| 524 | // EnC added fields aren't at a simple offset like normal fields. |
| 525 | if (dwOffset == FIELD_OFFSET_NEW_ENC) // IsEnCNew() |
| 526 | { |
| 527 | // We'll have to go through some effort to compute the address of this field. |
| 528 | return ((EnCFieldDesc *)this)->GetAddress(OBJECTREFToObject(o)); |
| 529 | } |
| 530 | #endif |
| 531 | |
| 532 | return (void *) (dac_cast<TADDR>(o->GetData()) + dwOffset); |
| 533 | } |
| 534 | |
| 535 | // And here's the equivalent, when you are guaranteed that the enclosing instance of |
| 536 | // the field is in the GC Heap. So if the enclosing instance is a value type, it had |
| 537 | // better be boxed. We ASSERT this. |
| 538 | void *FieldDesc::GetAddressGuaranteedInHeap(void *o) |
| 539 | { |
| 540 | CONTRACTL |
| 541 | { |
| 542 | NOTHROW; |
| 543 | GC_NOTRIGGER; |
| 544 | MODE_COOPERATIVE; |
| 545 | SO_TOLERANT; |
| 546 | } |
| 547 | CONTRACTL_END; |
| 548 | |
| 549 | _ASSERTE(!IsEnCNew()); |
| 550 | |
| 551 | return ((BYTE*)(o)) + sizeof(Object) + m_dwOffset; |
| 552 | } |
| 553 | |
| 554 | |
| 555 | DWORD FieldDesc::GetValue32(OBJECTREF o) |
| 556 | { |
| 557 | WRAPPER_NO_CONTRACT; |
| 558 | |
| 559 | DWORD val; |
| 560 | GetInstanceField(o, (LPVOID)&val); |
| 561 | return val; |
| 562 | } |
| 563 | |
| 564 | #ifndef DACCESS_COMPILE |
| 565 | VOID FieldDesc::SetValue32(OBJECTREF o, DWORD dwValue) |
| 566 | { |
| 567 | CONTRACTL |
| 568 | { |
| 569 | THROWS; |
| 570 | GC_TRIGGERS; |
| 571 | MODE_COOPERATIVE; |
| 572 | } |
| 573 | CONTRACTL_END; |
| 574 | |
| 575 | SetInstanceField(o, (LPVOID)&dwValue); |
| 576 | } |
| 577 | #endif // #ifndef DACCESS_COMPILE |
| 578 | |
| 579 | void* FieldDesc::GetValuePtr(OBJECTREF o) |
| 580 | { |
| 581 | WRAPPER_NO_CONTRACT; |
| 582 | |
| 583 | void* val; |
| 584 | GetInstanceField(o, (LPVOID)&val); |
| 585 | return val; |
| 586 | } |
| 587 | |
| 588 | #ifndef DACCESS_COMPILE |
| 589 | VOID FieldDesc::SetValuePtr(OBJECTREF o, void* pValue) |
| 590 | { |
| 591 | WRAPPER_NO_CONTRACT; |
| 592 | |
| 593 | SetInstanceField(o, (LPVOID)&pValue); |
| 594 | } |
| 595 | #endif // #ifndef DACCESS_COMPILE |
| 596 | |
| 597 | OBJECTREF FieldDesc::GetRefValue(OBJECTREF o) |
| 598 | { |
| 599 | WRAPPER_NO_CONTRACT; |
| 600 | |
| 601 | OBJECTREF val = NULL; |
| 602 | |
| 603 | #ifdef PROFILING_SUPPORTED |
| 604 | GCPROTECT_BEGIN(val); |
| 605 | #endif |
| 606 | |
| 607 | GetInstanceField(o, (LPVOID)&val); |
| 608 | |
| 609 | #ifdef PROFILING_SUPPORTED |
| 610 | GCPROTECT_END(); |
| 611 | #endif |
| 612 | |
| 613 | return val; |
| 614 | } |
| 615 | |
| 616 | #ifndef DACCESS_COMPILE |
| 617 | VOID FieldDesc::SetRefValue(OBJECTREF o, OBJECTREF orValue) |
| 618 | { |
| 619 | CONTRACTL |
| 620 | { |
| 621 | THROWS; |
| 622 | GC_TRIGGERS; |
| 623 | MODE_COOPERATIVE; |
| 624 | } |
| 625 | CONTRACTL_END; |
| 626 | VALIDATEOBJECTREF(o); |
| 627 | VALIDATEOBJECTREF(orValue); |
| 628 | |
| 629 | SetInstanceField(o, (LPVOID)&orValue); |
| 630 | } |
| 631 | #endif // #ifndef DACCESS_COMPILE |
| 632 | |
| 633 | USHORT FieldDesc::GetValue16(OBJECTREF o) |
| 634 | { |
| 635 | WRAPPER_NO_CONTRACT; |
| 636 | |
| 637 | USHORT val; |
| 638 | GetInstanceField(o, (LPVOID)&val); |
| 639 | return val; |
| 640 | } |
| 641 | |
| 642 | #ifndef DACCESS_COMPILE |
| 643 | VOID FieldDesc::SetValue16(OBJECTREF o, DWORD dwValue) |
| 644 | { |
| 645 | CONTRACTL |
| 646 | { |
| 647 | THROWS; |
| 648 | GC_TRIGGERS; |
| 649 | MODE_COOPERATIVE; |
| 650 | } |
| 651 | CONTRACTL_END; |
| 652 | |
| 653 | USHORT val = (USHORT)dwValue; |
| 654 | SetInstanceField(o, (LPVOID)&val); |
| 655 | } |
| 656 | #endif // #ifndef DACCESS_COMPILE |
| 657 | |
| 658 | BYTE FieldDesc::GetValue8(OBJECTREF o) |
| 659 | { |
| 660 | WRAPPER_NO_CONTRACT; |
| 661 | |
| 662 | BYTE val; |
| 663 | GetInstanceField(o, (LPVOID)&val); |
| 664 | return val; |
| 665 | |
| 666 | } |
| 667 | |
| 668 | #ifndef DACCESS_COMPILE |
| 669 | VOID FieldDesc::SetValue8(OBJECTREF o, DWORD dwValue) |
| 670 | { |
| 671 | CONTRACTL |
| 672 | { |
| 673 | THROWS; |
| 674 | GC_TRIGGERS; |
| 675 | MODE_COOPERATIVE; |
| 676 | } |
| 677 | CONTRACTL_END; |
| 678 | |
| 679 | BYTE val = (BYTE)dwValue; |
| 680 | SetInstanceField(o, (LPVOID)&val); |
| 681 | } |
| 682 | #endif // #ifndef DACCESS_COMPILE |
| 683 | |
| 684 | __int64 FieldDesc::GetValue64(OBJECTREF o) |
| 685 | { |
| 686 | WRAPPER_NO_CONTRACT; |
| 687 | __int64 val; |
| 688 | GetInstanceField(o, (LPVOID)&val); |
| 689 | return val; |
| 690 | |
| 691 | } |
| 692 | |
| 693 | #ifndef DACCESS_COMPILE |
| 694 | VOID FieldDesc::SetValue64(OBJECTREF o, __int64 value) |
| 695 | { |
| 696 | CONTRACTL |
| 697 | { |
| 698 | THROWS; |
| 699 | GC_TRIGGERS; |
| 700 | MODE_COOPERATIVE; |
| 701 | } |
| 702 | CONTRACTL_END; |
| 703 | SetInstanceField(o, (LPVOID)&value); |
| 704 | } |
| 705 | #endif // #ifndef DACCESS_COMPILE |
| 706 | |
| 707 | #endif // !CROSSGEN_COMPILE |
| 708 | |
| 709 | |
| 710 | #ifndef DACCESS_COMPILE |
| 711 | |
| 712 | #ifdef FEATURE_NATIVE_IMAGE_GENERATION |
| 713 | void FieldDesc::SaveContents(DataImage *image) |
| 714 | { |
| 715 | STANDARD_VM_CONTRACT; |
| 716 | |
| 717 | #ifdef _DEBUG |
| 718 | if (m_debugName && !image->IsStored((void*) m_debugName)) |
| 719 | image->StoreStructure((void *) m_debugName, |
| 720 | (ULONG)(strlen(m_debugName) + 1), |
| 721 | DataImage::ITEM_DEBUG, |
| 722 | 1); |
| 723 | #endif |
| 724 | |
| 725 | // |
| 726 | // If we are compiling an IL only image, and our RVA fits |
| 727 | // in the designated range, copy the RVA data over to the prejit |
| 728 | // image. |
| 729 | // |
| 730 | |
| 731 | if (IsRVA()) |
| 732 | { |
| 733 | // |
| 734 | // Move the RVA data into the prejit image. |
| 735 | // |
| 736 | |
| 737 | UINT size = LoadSize(); |
| 738 | |
| 739 | // |
| 740 | // Compute an alignment for the data based on the alignment |
| 741 | // of the RVA. We'll align up to 8 bytes. |
| 742 | // |
| 743 | |
| 744 | UINT align = 1; |
| 745 | DWORD rva = GetOffset(); |
| 746 | DWORD rvaTemp = rva; |
| 747 | |
| 748 | while ((rvaTemp&1) == 0 && align < 8 && align < size) |
| 749 | { |
| 750 | align <<= 1; |
| 751 | rvaTemp >>= 1; |
| 752 | } |
| 753 | |
| 754 | image->StoreRvaInfo(this, |
| 755 | rva, |
| 756 | size, |
| 757 | align); |
| 758 | } |
| 759 | } |
| 760 | |
| 761 | void FieldDesc::Fixup(DataImage *image) |
| 762 | { |
| 763 | STANDARD_VM_CONTRACT; |
| 764 | |
| 765 | LOG((LF_ZAP, LL_INFO10000, "FieldDesc::Fixup %s::%s\n" , GetApproxEnclosingMethodTable()->GetDebugClassName(), m_debugName)); |
| 766 | image->FixupRelativePointerField(this, offsetof(FieldDesc, m_pMTOfEnclosingClass)); |
| 767 | |
| 768 | #ifdef _DEBUG |
| 769 | image->FixupPointerField(this, offsetof(FieldDesc, m_debugName)); |
| 770 | #endif |
| 771 | |
| 772 | // if (IsRVAFieldWithLessThanBigOffset()) |
| 773 | // { |
| 774 | // offset of RVA fields is fixed up in DataImage::FixupRvaStructure |
| 775 | // } |
| 776 | } |
| 777 | #endif // FEATURE_NATIVE_IMAGE_GENERATION |
| 778 | |
| 779 | #endif // #ifndef DACCESS_COMPILE |
| 780 | |
| 781 | UINT FieldDesc::LoadSize() |
| 782 | { |
| 783 | CONTRACTL |
| 784 | { |
| 785 | INSTANCE_CHECK; |
| 786 | THROWS; |
| 787 | GC_TRIGGERS; |
| 788 | MODE_ANY; |
| 789 | } |
| 790 | CONTRACTL_END |
| 791 | |
| 792 | CorElementType type = GetFieldType(); |
| 793 | UINT size = GetSizeForCorElementType(type); |
| 794 | if (size == (UINT) -1) |
| 795 | { |
| 796 | // LOG((LF_CLASSLOADER, LL_INFO10000, "FieldDesc::LoadSize %s::%s\n", GetApproxEnclosingMethodTable()->GetDebugClassName(), m_debugName)); |
| 797 | CONSISTENCY_CHECK(GetFieldType() == ELEMENT_TYPE_VALUETYPE); |
| 798 | size = GetApproxFieldTypeHandleThrowing().GetMethodTable()->GetNumInstanceFieldBytes(); |
| 799 | } |
| 800 | |
| 801 | return size; |
| 802 | } |
| 803 | |
| 804 | UINT FieldDesc::GetSize() |
| 805 | { |
| 806 | CONTRACTL |
| 807 | { |
| 808 | INSTANCE_CHECK; |
| 809 | NOTHROW; |
| 810 | GC_NOTRIGGER; |
| 811 | MODE_ANY; |
| 812 | FORBID_FAULT; |
| 813 | } |
| 814 | CONTRACTL_END |
| 815 | |
| 816 | CorElementType type = GetFieldType(); |
| 817 | UINT size = GetSizeForCorElementType(type); |
| 818 | if (size == (UINT) -1) |
| 819 | { |
| 820 | LOG((LF_CLASSLOADER, LL_INFO10000, "FieldDesc::GetSize %s::%s\n" , GetApproxEnclosingMethodTable()->GetDebugClassName(), m_debugName)); |
| 821 | CONSISTENCY_CHECK(GetFieldType() == ELEMENT_TYPE_VALUETYPE); |
| 822 | TypeHandle t = LookupApproxFieldTypeHandle(); |
| 823 | if (!t.IsNull()) |
| 824 | { |
| 825 | size = t.GetMethodTable()->GetNumInstanceFieldBytes(); |
| 826 | } |
| 827 | } |
| 828 | |
| 829 | return size; |
| 830 | } |
| 831 | |
| 832 | // See field.h for details |
| 833 | Instantiation FieldDesc::GetExactClassInstantiation(TypeHandle possibleObjType) |
| 834 | { |
| 835 | WRAPPER_NO_CONTRACT; |
| 836 | |
| 837 | // We know that it isn't going to be null here. Tell PREFIX that we know it. |
| 838 | PREFIX_ASSUME(GetApproxEnclosingMethodTable()!=NULL); |
| 839 | if (possibleObjType.IsNull()) |
| 840 | { |
| 841 | return GetApproxEnclosingMethodTable()->GetInstantiation(); |
| 842 | } |
| 843 | else |
| 844 | { |
| 845 | PREFIX_ASSUME(GetApproxEnclosingMethodTable()!=NULL); |
| 846 | return possibleObjType.GetInstantiationOfParentClass(GetApproxEnclosingMethodTable()); |
| 847 | } |
| 848 | } |
| 849 | |
| 850 | // Given, { List<String>, List<__Canon>._items } where _items is of type T[], |
| 851 | // it returns String[]. |
| 852 | |
| 853 | TypeHandle FieldDesc::GetExactFieldType(TypeHandle owner) |
| 854 | { |
| 855 | CONTRACT(TypeHandle) |
| 856 | { |
| 857 | THROWS; |
| 858 | GC_TRIGGERS; |
| 859 | MODE_ANY; |
| 860 | PRECONDITION(CheckPointer(owner, NULL_OK)); |
| 861 | POSTCONDITION(CheckPointer(RETVAL)); |
| 862 | } |
| 863 | CONTRACT_END |
| 864 | |
| 865 | if (GetApproxEnclosingMethodTable() == owner.AsMethodTable()) |
| 866 | { |
| 867 | //Yes, this is exactly the type I was looking for. |
| 868 | RETURN(GetFieldTypeHandleThrowing()); |
| 869 | } |
| 870 | else |
| 871 | { |
| 872 | //This FieldDesc doesn't exactly represent the owner type. Go look up the exact type. |
| 873 | |
| 874 | // We need to figure out the precise type of the field. |
| 875 | // First, get the signature of the field |
| 876 | PCCOR_SIGNATURE pSig; |
| 877 | DWORD cSig; |
| 878 | GetSig(&pSig, &cSig); |
| 879 | SigPointer sig(pSig, cSig); |
| 880 | |
| 881 | ULONG callConv; |
| 882 | IfFailThrow(sig.GetCallingConv(&callConv)); |
| 883 | _ASSERTE(callConv == IMAGE_CEE_CS_CALLCONV_FIELD); |
| 884 | |
| 885 | // Get the generics information |
| 886 | SigTypeContext sigTypeContext(GetExactClassInstantiation(owner), Instantiation()); |
| 887 | |
| 888 | // Load the exact type |
| 889 | RETURN (sig.GetTypeHandleThrowing(GetModule(), &sigTypeContext)); |
| 890 | } |
| 891 | } |
| 892 | |
| 893 | #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) |
| 894 | REFLECTFIELDREF FieldDesc::GetStubFieldInfo() |
| 895 | { |
| 896 | CONTRACTL |
| 897 | { |
| 898 | THROWS; |
| 899 | GC_TRIGGERS; |
| 900 | INJECT_FAULT(COMPlusThrowOM()); |
| 901 | MODE_COOPERATIVE; |
| 902 | } |
| 903 | CONTRACTL_END; |
| 904 | |
| 905 | REFLECTFIELDREF retVal; |
| 906 | REFLECTFIELDREF fieldRef = (REFLECTFIELDREF)AllocateObject(MscorlibBinder::GetClass(CLASS__STUBFIELDINFO)); |
| 907 | GCPROTECT_BEGIN(fieldRef); |
| 908 | |
| 909 | fieldRef->SetField(this); |
| 910 | LoaderAllocator *pLoaderAllocatorOfMethod = this->GetApproxEnclosingMethodTable()->GetLoaderAllocator(); |
| 911 | if (pLoaderAllocatorOfMethod->IsCollectible()) |
| 912 | fieldRef->SetKeepAlive(pLoaderAllocatorOfMethod->GetExposedObject()); |
| 913 | |
| 914 | retVal = fieldRef; |
| 915 | GCPROTECT_END(); |
| 916 | |
| 917 | return retVal; |
| 918 | } |
| 919 | #endif // !DACCESS_COMPILE && !CROSSGEN_COMPILE |
| 920 | |