1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | // |
5 | // |
6 | |
7 | #include "common.h" |
8 | #include "reflectioninvocation.h" |
9 | #include "invokeutil.h" |
10 | #include "object.h" |
11 | #include "class.h" |
12 | #include "method.hpp" |
13 | #include "typehandle.h" |
14 | #include "field.h" |
15 | #include "eeconfig.h" |
16 | #include "vars.hpp" |
17 | #include "jitinterface.h" |
18 | #include "contractimpl.h" |
19 | #include "virtualcallstub.h" |
20 | #include "comdelegate.h" |
21 | #include "generics.h" |
22 | |
23 | #ifdef FEATURE_COMINTEROP |
24 | #include "interoputil.h" |
25 | #include "runtimecallablewrapper.h" |
26 | #endif |
27 | |
28 | #include "dbginterface.h" |
29 | #include "argdestination.h" |
30 | |
31 | /**************************************************************************/ |
32 | /* if the type handle 'th' is a byref to a nullable type, return the |
33 | type handle to the nullable type in the byref. Otherwise return |
34 | the null type handle */ |
35 | static TypeHandle NullableTypeOfByref(TypeHandle th) { |
36 | CONTRACTL |
37 | { |
38 | NOTHROW; |
39 | GC_NOTRIGGER; |
40 | SO_TOLERANT; |
41 | MODE_ANY; |
42 | } |
43 | CONTRACTL_END; |
44 | |
45 | if (th.GetVerifierCorElementType() != ELEMENT_TYPE_BYREF) |
46 | return TypeHandle(); |
47 | |
48 | TypeHandle subType = th.AsTypeDesc()->GetTypeParam(); |
49 | if (!Nullable::IsNullableType(subType)) |
50 | return TypeHandle(); |
51 | |
52 | return subType; |
53 | } |
54 | |
55 | static void TryCallMethodWorker(MethodDescCallSite* pMethodCallSite, ARG_SLOT* args, Frame* pDebuggerCatchFrame) |
56 | { |
57 | // Use static contracts b/c we have SEH. |
58 | STATIC_CONTRACT_THROWS; |
59 | STATIC_CONTRACT_GC_TRIGGERS; |
60 | STATIC_CONTRACT_MODE_ANY; |
61 | |
62 | struct Param: public NotifyOfCHFFilterWrapperParam |
63 | { |
64 | MethodDescCallSite * pMethodCallSite; |
65 | ARG_SLOT* args; |
66 | } param; |
67 | |
68 | param.pFrame = pDebuggerCatchFrame; |
69 | param.pMethodCallSite = pMethodCallSite; |
70 | param.args = args; |
71 | |
72 | PAL_TRY(Param *, pParam, ¶m) |
73 | { |
74 | pParam->pMethodCallSite->CallWithValueTypes(pParam->args); |
75 | } |
76 | PAL_EXCEPT_FILTER(NotifyOfCHFFilterWrapper) |
77 | { |
78 | // Should never reach here b/c handler should always continue search. |
79 | _ASSERTE(false); |
80 | } |
81 | PAL_ENDTRY |
82 | } |
83 | |
84 | // Warning: This method has subtle differences from CallDescrWorkerReflectionWrapper |
85 | // In particular that one captures watson bucket data and corrupting exception severity, |
86 | // then transfers that data to the newly produced TargetInvocationException. This one |
87 | // doesn't take those same steps. |
88 | // |
89 | static void TryCallMethod(MethodDescCallSite* pMethodCallSite, ARG_SLOT* args, bool wrapExceptions) { |
90 | CONTRACTL { |
91 | THROWS; |
92 | GC_TRIGGERS; |
93 | MODE_COOPERATIVE; |
94 | } |
95 | CONTRACTL_END; |
96 | |
97 | if (wrapExceptions) |
98 | { |
99 | OBJECTREF ppException = NULL; |
100 | GCPROTECT_BEGIN(ppException); |
101 | |
102 | // The sole purpose of having this frame is to tell the debugger that we have a catch handler here |
103 | // which may swallow managed exceptions. The debugger needs this in order to send a |
104 | // CatchHandlerFound (CHF) notification. |
105 | FrameWithCookie<DebuggerU2MCatchHandlerFrame> catchFrame; |
106 | EX_TRY{ |
107 | TryCallMethodWorker(pMethodCallSite, args, &catchFrame); |
108 | } |
109 | EX_CATCH{ |
110 | ppException = GET_THROWABLE(); |
111 | _ASSERTE(ppException); |
112 | } |
113 | EX_END_CATCH(RethrowTransientExceptions) |
114 | catchFrame.Pop(); |
115 | |
116 | // It is important to re-throw outside the catch block because re-throwing will invoke |
117 | // the jitter and managed code and will cause us to use more than the backout stack limit. |
118 | if (ppException != NULL) |
119 | { |
120 | // If we get here we need to throw an TargetInvocationException |
121 | OBJECTREF except = InvokeUtil::CreateTargetExcept(&ppException); |
122 | COMPlusThrow(except); |
123 | } |
124 | GCPROTECT_END(); |
125 | } |
126 | else |
127 | { |
128 | pMethodCallSite->CallWithValueTypes(args); |
129 | } |
130 | } |
131 | |
132 | |
133 | |
134 | |
135 | FCIMPL5(Object*, RuntimeFieldHandle::GetValue, ReflectFieldObject *pFieldUNSAFE, Object *instanceUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, ReflectClassBaseObject *pDeclaringTypeUNSAFE, CLR_BOOL *pDomainInitialized) { |
136 | CONTRACTL { |
137 | FCALL_CHECK; |
138 | } |
139 | CONTRACTL_END; |
140 | |
141 | struct _gc |
142 | { |
143 | OBJECTREF target; |
144 | REFLECTCLASSBASEREF pFieldType; |
145 | REFLECTCLASSBASEREF pDeclaringType; |
146 | REFLECTFIELDREF refField; |
147 | }gc; |
148 | |
149 | gc.target = ObjectToOBJECTREF(instanceUNSAFE); |
150 | gc.pFieldType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pFieldTypeUNSAFE); |
151 | gc.pDeclaringType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pDeclaringTypeUNSAFE); |
152 | gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(pFieldUNSAFE); |
153 | |
154 | if ((gc.pFieldType == NULL) || (gc.refField == NULL)) |
155 | FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle" )); |
156 | |
157 | TypeHandle fieldType = gc.pFieldType->GetType(); |
158 | TypeHandle declaringType = (gc.pDeclaringType != NULL) ? gc.pDeclaringType->GetType() : TypeHandle(); |
159 | |
160 | Assembly *pAssem; |
161 | if (declaringType.IsNull()) |
162 | { |
163 | // global field |
164 | pAssem = gc.refField->GetField()->GetModule()->GetAssembly(); |
165 | } |
166 | else |
167 | { |
168 | pAssem = declaringType.GetAssembly(); |
169 | } |
170 | |
171 | OBJECTREF rv = NULL; // not protected |
172 | |
173 | HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); |
174 | // There can be no GC after this until the Object is returned. |
175 | rv = InvokeUtil::GetFieldValue(gc.refField->GetField(), fieldType, &gc.target, declaringType, pDomainInitialized); |
176 | HELPER_METHOD_FRAME_END(); |
177 | |
178 | return OBJECTREFToObject(rv); |
179 | } |
180 | FCIMPLEND |
181 | |
182 | FCIMPL2(FC_BOOL_RET, ReflectionInvocation::CanValueSpecialCast, ReflectClassBaseObject *pValueTypeUNSAFE, ReflectClassBaseObject *pTargetTypeUNSAFE) { |
183 | CONTRACTL { |
184 | FCALL_CHECK; |
185 | PRECONDITION(CheckPointer(pValueTypeUNSAFE)); |
186 | PRECONDITION(CheckPointer(pTargetTypeUNSAFE)); |
187 | } |
188 | CONTRACTL_END; |
189 | |
190 | REFLECTCLASSBASEREF refValueType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pValueTypeUNSAFE); |
191 | REFLECTCLASSBASEREF refTargetType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTargetTypeUNSAFE); |
192 | |
193 | TypeHandle valueType = refValueType->GetType(); |
194 | TypeHandle targetType = refTargetType->GetType(); |
195 | |
196 | // we are here only if the target type is a primitive, an enum or a pointer |
197 | |
198 | CorElementType targetCorElement = targetType.GetVerifierCorElementType(); |
199 | |
200 | BOOL ret = TRUE; |
201 | HELPER_METHOD_FRAME_BEGIN_RET_2(refValueType, refTargetType); |
202 | // the field type is a pointer |
203 | if (targetCorElement == ELEMENT_TYPE_PTR || targetCorElement == ELEMENT_TYPE_FNPTR) { |
204 | // the object must be an IntPtr or a System.Reflection.Pointer |
205 | if (valueType == TypeHandle(MscorlibBinder::GetClass(CLASS__INTPTR))) { |
206 | // |
207 | // it's an IntPtr, it's good. |
208 | } |
209 | // |
210 | // it's a System.Reflection.Pointer object |
211 | |
212 | // void* assigns to any pointer. Otherwise the type of the pointer must match |
213 | else if (!InvokeUtil::IsVoidPtr(targetType)) { |
214 | if (!valueType.CanCastTo(targetType)) |
215 | ret = FALSE; |
216 | } |
217 | } else { |
218 | // the field type is an enum or a primitive. To have any chance of assignement the object type must |
219 | // be an enum or primitive as well. |
220 | // So get the internal cor element and that must be the same or widen |
221 | CorElementType valueCorElement = valueType.GetVerifierCorElementType(); |
222 | if (InvokeUtil::IsPrimitiveType(valueCorElement)) |
223 | ret = (InvokeUtil::CanPrimitiveWiden(targetCorElement, valueCorElement)) ? TRUE : FALSE; |
224 | else |
225 | ret = FALSE; |
226 | } |
227 | HELPER_METHOD_FRAME_END(); |
228 | FC_RETURN_BOOL(ret); |
229 | } |
230 | FCIMPLEND |
231 | |
232 | FCIMPL3(Object*, ReflectionInvocation::AllocateValueType, ReflectClassBaseObject *pTargetTypeUNSAFE, Object *valueUNSAFE, CLR_BOOL fForceTypeChange) { |
233 | CONTRACTL { |
234 | FCALL_CHECK; |
235 | PRECONDITION(CheckPointer(pTargetTypeUNSAFE)); |
236 | PRECONDITION(CheckPointer(valueUNSAFE, NULL_OK)); |
237 | } |
238 | CONTRACTL_END; |
239 | |
240 | struct _gc |
241 | { |
242 | REFLECTCLASSBASEREF refTargetType; |
243 | OBJECTREF value; |
244 | OBJECTREF obj; |
245 | }gc; |
246 | |
247 | gc.value = ObjectToOBJECTREF(valueUNSAFE); |
248 | gc.obj = gc.value; |
249 | gc.refTargetType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTargetTypeUNSAFE); |
250 | |
251 | TypeHandle targetType = gc.refTargetType->GetType(); |
252 | |
253 | HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); |
254 | CorElementType targetElementType = targetType.GetSignatureCorElementType(); |
255 | if (InvokeUtil::IsPrimitiveType(targetElementType) || targetElementType == ELEMENT_TYPE_VALUETYPE) |
256 | { |
257 | MethodTable* allocMT = targetType.AsMethodTable(); |
258 | if (gc.value != NULL) |
259 | { |
260 | // ignore the type of the incoming box if fForceTypeChange is set |
261 | // and the target type is not nullable |
262 | if (!fForceTypeChange || Nullable::IsNullableType(targetType)) |
263 | allocMT = gc.value->GetMethodTable(); |
264 | } |
265 | |
266 | // for null Nullable<T> we don't want a default value being created. |
267 | // just allow the null value to be passed, as it will be converted to |
268 | // a true nullable |
269 | if (!(gc.value == NULL && Nullable::IsNullableType(targetType))) |
270 | { |
271 | // boxed value type are 'read-only' in the sence that you can't |
272 | // only the implementor of the value type can expose mutators. |
273 | // To insure byrefs don't mutate value classes in place, we make |
274 | // a copy (and if we were not given one, we create a null value type |
275 | // instance. |
276 | gc.obj = allocMT->Allocate(); |
277 | |
278 | if (gc.value != NULL) |
279 | CopyValueClassUnchecked(gc.obj->UnBox(), gc.value->UnBox(), allocMT); |
280 | } |
281 | } |
282 | |
283 | HELPER_METHOD_FRAME_END(); |
284 | |
285 | return OBJECTREFToObject(gc.obj); |
286 | } |
287 | FCIMPLEND |
288 | |
289 | FCIMPL7(void, RuntimeFieldHandle::SetValue, ReflectFieldObject *pFieldUNSAFE, Object *targetUNSAFE, Object *valueUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, DWORD attr, ReflectClassBaseObject *pDeclaringTypeUNSAFE, CLR_BOOL *pDomainInitialized) { |
290 | CONTRACTL { |
291 | FCALL_CHECK; |
292 | } |
293 | CONTRACTL_END; |
294 | |
295 | struct _gc { |
296 | OBJECTREF target; |
297 | OBJECTREF value; |
298 | REFLECTCLASSBASEREF fieldType; |
299 | REFLECTCLASSBASEREF declaringType; |
300 | REFLECTFIELDREF refField; |
301 | } gc; |
302 | |
303 | gc.target = ObjectToOBJECTREF(targetUNSAFE); |
304 | gc.value = ObjectToOBJECTREF(valueUNSAFE); |
305 | gc.fieldType= (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pFieldTypeUNSAFE); |
306 | gc.declaringType= (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pDeclaringTypeUNSAFE); |
307 | gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(pFieldUNSAFE); |
308 | |
309 | if ((gc.fieldType == NULL) || (gc.refField == NULL)) |
310 | FCThrowResVoid(kArgumentNullException, W("Arg_InvalidHandle" )); |
311 | |
312 | TypeHandle fieldType = gc.fieldType->GetType(); |
313 | TypeHandle declaringType = gc.declaringType != NULL ? gc.declaringType->GetType() : TypeHandle(); |
314 | |
315 | Assembly *pAssem; |
316 | if (declaringType.IsNull()) |
317 | { |
318 | // global field |
319 | pAssem = gc.refField->GetField()->GetModule()->GetAssembly(); |
320 | } |
321 | else |
322 | { |
323 | pAssem = declaringType.GetAssembly(); |
324 | } |
325 | |
326 | FC_GC_POLL_NOT_NEEDED(); |
327 | |
328 | FieldDesc* pFieldDesc = gc.refField->GetField(); |
329 | |
330 | HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); |
331 | |
332 | // Verify we're not trying to set the value of a static initonly field |
333 | // once the class has been initialized. |
334 | if (pFieldDesc->IsStatic()) |
335 | { |
336 | MethodTable* pEnclosingMT = pFieldDesc->GetEnclosingMethodTable(); |
337 | if (pEnclosingMT->IsClassInited() && IsFdInitOnly(pFieldDesc->GetAttributes())) |
338 | { |
339 | DefineFullyQualifiedNameForClassW(); |
340 | SString ssFieldName(SString::Utf8, pFieldDesc->GetName()); |
341 | COMPlusThrow(kFieldAccessException, |
342 | IDS_EE_CANNOT_SET_INITONLY_STATIC_FIELD, |
343 | ssFieldName.GetUnicode(), |
344 | GetFullyQualifiedNameForClassW(pEnclosingMT)); |
345 | } |
346 | } |
347 | |
348 | //TODO: cleanup this function |
349 | InvokeUtil::SetValidField(fieldType.GetSignatureCorElementType(), fieldType, pFieldDesc, &gc.target, &gc.value, declaringType, pDomainInitialized); |
350 | |
351 | HELPER_METHOD_FRAME_END(); |
352 | } |
353 | FCIMPLEND |
354 | |
355 | //A.CI work |
356 | FCIMPL1(Object*, RuntimeTypeHandle::Allocate, ReflectClassBaseObject* pTypeUNSAFE) |
357 | { |
358 | CONTRACTL { |
359 | FCALL_CHECK; |
360 | PRECONDITION(CheckPointer(pTypeUNSAFE)); |
361 | } |
362 | CONTRACTL_END |
363 | |
364 | REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); |
365 | TypeHandle type = refType->GetType(); |
366 | |
367 | // Handle the nullable<T> special case |
368 | if (Nullable::IsNullableType(type)) { |
369 | return OBJECTREFToObject(Nullable::BoxedNullableNull(type)); |
370 | } |
371 | |
372 | OBJECTREF rv = NULL; |
373 | HELPER_METHOD_FRAME_BEGIN_RET_1(refType); |
374 | rv = AllocateObject(type.GetMethodTable()); |
375 | HELPER_METHOD_FRAME_END(); |
376 | return OBJECTREFToObject(rv); |
377 | |
378 | }//Allocate |
379 | FCIMPLEND |
380 | |
381 | FCIMPL6(Object*, RuntimeTypeHandle::CreateInstance, ReflectClassBaseObject* refThisUNSAFE, |
382 | CLR_BOOL publicOnly, |
383 | CLR_BOOL wrapExceptions, |
384 | CLR_BOOL* pbCanBeCached, |
385 | MethodDesc** pConstructor, |
386 | CLR_BOOL* pbHasNoDefaultCtor) { |
387 | CONTRACTL { |
388 | FCALL_CHECK; |
389 | PRECONDITION(CheckPointer(refThisUNSAFE)); |
390 | PRECONDITION(CheckPointer(pbCanBeCached)); |
391 | PRECONDITION(CheckPointer(pConstructor)); |
392 | PRECONDITION(CheckPointer(pbHasNoDefaultCtor)); |
393 | PRECONDITION(*pbCanBeCached == false); |
394 | PRECONDITION(*pConstructor == NULL); |
395 | PRECONDITION(*pbHasNoDefaultCtor == false); |
396 | } |
397 | CONTRACTL_END; |
398 | |
399 | if (refThisUNSAFE == NULL) |
400 | FCThrow(kNullReferenceException); |
401 | |
402 | MethodDesc* pMeth; |
403 | |
404 | OBJECTREF rv = NULL; |
405 | REFLECTCLASSBASEREF refThis = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(refThisUNSAFE); |
406 | TypeHandle thisTH = refThis->GetType(); |
407 | |
408 | Assembly *pAssem = thisTH.GetAssembly(); |
409 | |
410 | HELPER_METHOD_FRAME_BEGIN_RET_2(rv, refThis); |
411 | |
412 | MethodTable* pVMT; |
413 | |
414 | // Get the type information associated with refThis |
415 | if (thisTH.IsNull() || thisTH.IsTypeDesc()) { |
416 | *pbHasNoDefaultCtor = true; |
417 | goto DoneCreateInstance; |
418 | } |
419 | |
420 | pVMT = thisTH.AsMethodTable(); |
421 | |
422 | pVMT->EnsureInstanceActive(); |
423 | |
424 | #ifdef FEATURE_COMINTEROP |
425 | // If this is __ComObject then create the underlying COM object. |
426 | if (IsComObjectClass(refThis->GetType())) { |
427 | #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION |
428 | SyncBlock* pSyncBlock = refThis->GetSyncBlock(); |
429 | |
430 | void* pClassFactory = (void*)pSyncBlock->GetInteropInfo()->GetComClassFactory(); |
431 | if (!pClassFactory) |
432 | COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY); |
433 | |
434 | // create an instance of the Com Object |
435 | rv = ((ComClassFactory*)pClassFactory)->CreateInstance(NULL); |
436 | |
437 | #else // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION |
438 | |
439 | COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY); |
440 | |
441 | #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION |
442 | } |
443 | else |
444 | #endif // FEATURE_COMINTEROP |
445 | { |
446 | // if this is an abstract class then we will fail this |
447 | if (pVMT->IsAbstract()) { |
448 | if (pVMT->IsInterface()) |
449 | COMPlusThrow(kMissingMethodException,W("Acc_CreateInterface" )); |
450 | else |
451 | COMPlusThrow(kMissingMethodException,W("Acc_CreateAbst" )); |
452 | } |
453 | else if (pVMT->ContainsGenericVariables()) { |
454 | COMPlusThrow(kArgumentException,W("Acc_CreateGeneric" )); |
455 | } |
456 | |
457 | if (pVMT->IsByRefLike()) |
458 | COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike" )); |
459 | |
460 | if (pVMT->IsSharedByGenericInstantiations()) |
461 | COMPlusThrow(kNotSupportedException, W("NotSupported_Type" )); |
462 | |
463 | if (!pVMT->HasDefaultConstructor()) |
464 | { |
465 | // We didn't find the parameterless constructor, |
466 | // if this is a Value class we can simply allocate one and return it |
467 | |
468 | if (!pVMT->IsValueType()) { |
469 | *pbHasNoDefaultCtor = true; |
470 | goto DoneCreateInstance; |
471 | } |
472 | |
473 | // Handle the nullable<T> special case |
474 | if (Nullable::IsNullableType(thisTH)) { |
475 | rv = Nullable::BoxedNullableNull(thisTH); |
476 | } |
477 | else |
478 | rv = pVMT->Allocate(); |
479 | |
480 | if (!pVMT->Collectible()) |
481 | { |
482 | *pbCanBeCached = true; |
483 | } |
484 | } |
485 | else // !pVMT->HasDefaultConstructor() |
486 | { |
487 | pMeth = pVMT->GetDefaultConstructor(); |
488 | |
489 | // Validate the method can be called by this caller |
490 | DWORD attr = pMeth->GetAttrs(); |
491 | |
492 | if (!IsMdPublic(attr) && publicOnly) { |
493 | *pbHasNoDefaultCtor = true; |
494 | goto DoneCreateInstance; |
495 | } |
496 | |
497 | // We've got the class, lets allocate it and call the constructor |
498 | OBJECTREF o; |
499 | bool remoting = false; |
500 | |
501 | o = AllocateObject(pVMT); |
502 | GCPROTECT_BEGIN(o); |
503 | |
504 | MethodDescCallSite ctor(pMeth, &o); |
505 | |
506 | // Copy "this" pointer |
507 | ARG_SLOT arg; |
508 | if (pVMT->IsValueType()) |
509 | arg = PtrToArgSlot(o->UnBox()); |
510 | else |
511 | arg = ObjToArgSlot(o); |
512 | |
513 | // Call the method |
514 | TryCallMethod(&ctor, &arg, wrapExceptions); |
515 | |
516 | rv = o; |
517 | GCPROTECT_END(); |
518 | |
519 | // No need to set these if they cannot be cached. In particular, if the type is a value type with a custom |
520 | // parameterless constructor, don't allow caching and have subsequent calls come back here to allocate an object and |
521 | // call the constructor. |
522 | if (!remoting && !pVMT->Collectible() && !pVMT->IsValueType()) |
523 | { |
524 | *pbCanBeCached = true; |
525 | *pConstructor = pMeth; |
526 | } |
527 | } |
528 | } |
529 | DoneCreateInstance: |
530 | HELPER_METHOD_FRAME_END(); |
531 | return OBJECTREFToObject(rv); |
532 | } |
533 | FCIMPLEND |
534 | |
535 | FCIMPL2(Object*, RuntimeTypeHandle::CreateInstanceForGenericType, ReflectClassBaseObject* pTypeUNSAFE, ReflectClassBaseObject* pParameterTypeUNSAFE) { |
536 | FCALL_CONTRACT; |
537 | |
538 | struct _gc |
539 | { |
540 | OBJECTREF rv; |
541 | REFLECTCLASSBASEREF refType; |
542 | REFLECTCLASSBASEREF refParameterType; |
543 | } gc; |
544 | |
545 | gc.rv = NULL; |
546 | gc.refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); |
547 | gc.refParameterType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pParameterTypeUNSAFE); |
548 | |
549 | MethodDesc* pMeth; |
550 | TypeHandle genericType = gc.refType->GetType(); |
551 | |
552 | TypeHandle parameterHandle = gc.refParameterType->GetType(); |
553 | |
554 | _ASSERTE (genericType.HasInstantiation()); |
555 | |
556 | HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); |
557 | |
558 | TypeHandle instantiatedType = ((TypeHandle)genericType.GetCanonicalMethodTable()).Instantiate(Instantiation(¶meterHandle, 1)); |
559 | |
560 | // Get the type information associated with refThis |
561 | MethodTable* pVMT = instantiatedType.GetMethodTable(); |
562 | _ASSERTE (pVMT != 0 && !instantiatedType.IsTypeDesc()); |
563 | _ASSERTE( !pVMT->IsAbstract() ||! instantiatedType.ContainsGenericVariables()); |
564 | _ASSERTE(!pVMT->IsByRefLike() && pVMT->HasDefaultConstructor()); |
565 | |
566 | pMeth = pVMT->GetDefaultConstructor(); |
567 | MethodDescCallSite ctor(pMeth); |
568 | |
569 | // We've got the class, lets allocate it and call the constructor |
570 | |
571 | // Nullables don't take this path, if they do we need special logic to make an instance |
572 | _ASSERTE(!Nullable::IsNullableType(instantiatedType)); |
573 | gc.rv = instantiatedType.GetMethodTable()->Allocate(); |
574 | |
575 | ARG_SLOT arg = ObjToArgSlot(gc.rv); |
576 | |
577 | // Call the method |
578 | TryCallMethod(&ctor, &arg, true); |
579 | |
580 | HELPER_METHOD_FRAME_END(); |
581 | return OBJECTREFToObject(gc.rv); |
582 | } |
583 | FCIMPLEND |
584 | |
585 | NOINLINE FC_BOOL_RET IsInstanceOfTypeHelper(OBJECTREF obj, REFLECTCLASSBASEREF refType) |
586 | { |
587 | FCALL_CONTRACT; |
588 | |
589 | BOOL canCast = false; |
590 | |
591 | FC_INNER_PROLOG(RuntimeTypeHandle::IsInstanceOfType); |
592 | |
593 | HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_2(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, obj, refType); |
594 | canCast = ObjIsInstanceOf(OBJECTREFToObject(obj), refType->GetType()); |
595 | HELPER_METHOD_FRAME_END(); |
596 | |
597 | FC_RETURN_BOOL(canCast); |
598 | } |
599 | |
600 | FCIMPL2(FC_BOOL_RET, RuntimeTypeHandle::IsInstanceOfType, ReflectClassBaseObject* pTypeUNSAFE, Object *objectUNSAFE) { |
601 | FCALL_CONTRACT; |
602 | |
603 | OBJECTREF obj = ObjectToOBJECTREF(objectUNSAFE); |
604 | REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); |
605 | |
606 | // Null is not instance of anything in reflection world |
607 | if (obj == NULL) |
608 | FC_RETURN_BOOL(false); |
609 | |
610 | if (refType == NULL) |
611 | FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle" )); |
612 | |
613 | switch (ObjIsInstanceOfNoGC(objectUNSAFE, refType->GetType())) { |
614 | case TypeHandle::CanCast: |
615 | FC_RETURN_BOOL(true); |
616 | case TypeHandle::CannotCast: |
617 | FC_RETURN_BOOL(false); |
618 | default: |
619 | // fall through to the slow helper |
620 | break; |
621 | } |
622 | |
623 | FC_INNER_RETURN(FC_BOOL_RET, IsInstanceOfTypeHelper(obj, refType)); |
624 | } |
625 | FCIMPLEND |
626 | |
627 | /****************************************************************************/ |
628 | /* boxed Nullable<T> are represented as a boxed T, so there is no unboxed |
629 | Nullable<T> inside to point at by reference. Because of this a byref |
630 | parameters of type Nullable<T> are copied out of the boxed instance |
631 | (to a place on the stack), before the call is made (and this copy is |
632 | pointed at). After the call returns, this copy must be copied back to |
633 | the original argument array. ByRefToNullable, is a simple linked list |
634 | that remembers what copy-backs are needed */ |
635 | |
636 | struct ByRefToNullable { |
637 | unsigned argNum; // The argument number for this byrefNullable argument |
638 | void* data; // The data to copy back to the ByRefNullable. This points to the stack |
639 | TypeHandle type; // The type of Nullable for this argument |
640 | ByRefToNullable* next; // list of these |
641 | |
642 | ByRefToNullable(unsigned aArgNum, void* aData, TypeHandle aType, ByRefToNullable* aNext) { |
643 | argNum = aArgNum; |
644 | data = aData; |
645 | type = aType; |
646 | next = aNext; |
647 | } |
648 | }; |
649 | |
650 | void CallDescrWorkerReflectionWrapper(CallDescrData * pCallDescrData, Frame * pFrame) |
651 | { |
652 | // Use static contracts b/c we have SEH. |
653 | STATIC_CONTRACT_THROWS; |
654 | STATIC_CONTRACT_GC_TRIGGERS; |
655 | STATIC_CONTRACT_MODE_ANY; |
656 | |
657 | struct Param: public NotifyOfCHFFilterWrapperParam |
658 | { |
659 | CallDescrData * pCallDescrData; |
660 | } param; |
661 | |
662 | param.pFrame = pFrame; |
663 | param.pCallDescrData = pCallDescrData; |
664 | |
665 | PAL_TRY(Param *, pParam, ¶m) |
666 | { |
667 | CallDescrWorkerWithHandler(pParam->pCallDescrData); |
668 | } |
669 | PAL_EXCEPT_FILTER(ReflectionInvocationExceptionFilter) |
670 | { |
671 | // Should never reach here b/c handler should always continue search. |
672 | _ASSERTE(false); |
673 | } |
674 | PAL_ENDTRY |
675 | } // CallDescrWorkerReflectionWrapper |
676 | |
677 | OBJECTREF InvokeArrayConstructor(ArrayTypeDesc* arrayDesc, MethodDesc* pMeth, PTRARRAYREF* objs, int argCnt) |
678 | { |
679 | CONTRACTL { |
680 | THROWS; |
681 | GC_TRIGGERS; |
682 | MODE_COOPERATIVE; |
683 | } |
684 | CONTRACTL_END; |
685 | |
686 | DWORD i; |
687 | |
688 | // If we're trying to create an array of pointers or function pointers, |
689 | // check that the caller has skip verification permission. |
690 | CorElementType et = arrayDesc->GetArrayElementTypeHandle().GetVerifierCorElementType(); |
691 | |
692 | // Validate the argCnt an the Rank. Also allow nested SZARRAY's. |
693 | _ASSERTE(argCnt == (int) arrayDesc->GetRank() || argCnt == (int) arrayDesc->GetRank() * 2 || |
694 | arrayDesc->GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY); |
695 | |
696 | // Validate all of the parameters. These all typed as integers |
697 | int allocSize = 0; |
698 | if (!ClrSafeInt<int>::multiply(sizeof(INT32), argCnt, allocSize)) |
699 | COMPlusThrow(kArgumentException, IDS_EE_SIGTOOCOMPLEX); |
700 | |
701 | INT32* indexes = (INT32*) _alloca((size_t)allocSize); |
702 | ZeroMemory(indexes, allocSize); |
703 | |
704 | for (i=0; i<(DWORD)argCnt; i++) |
705 | { |
706 | if (!(*objs)->m_Array[i]) |
707 | COMPlusThrowArgumentException(W("parameters" ), W("Arg_NullIndex" )); |
708 | |
709 | MethodTable* pMT = ((*objs)->m_Array[i])->GetMethodTable(); |
710 | CorElementType oType = TypeHandle(pMT).GetVerifierCorElementType(); |
711 | |
712 | if (!InvokeUtil::IsPrimitiveType(oType) || !InvokeUtil::CanPrimitiveWiden(ELEMENT_TYPE_I4,oType)) |
713 | COMPlusThrow(kArgumentException,W("Arg_PrimWiden" )); |
714 | |
715 | memcpy(&indexes[i],(*objs)->m_Array[i]->UnBox(),pMT->GetNumInstanceFieldBytes()); |
716 | } |
717 | |
718 | return AllocateArrayEx(TypeHandle(arrayDesc), indexes, argCnt); |
719 | } |
720 | |
721 | static BOOL IsActivationNeededForMethodInvoke(MethodDesc * pMD) |
722 | { |
723 | CONTRACTL { |
724 | THROWS; |
725 | GC_TRIGGERS; |
726 | MODE_COOPERATIVE; |
727 | } |
728 | CONTRACTL_END; |
729 | |
730 | // The activation for non-generic instance methods is covered by non-null "this pointer" |
731 | if (!pMD->IsStatic() && !pMD->HasMethodInstantiation() && !pMD->IsInterface()) |
732 | return FALSE; |
733 | |
734 | // We need to activate the instance at least once |
735 | pMD->EnsureActive(); |
736 | return FALSE; |
737 | } |
738 | |
739 | class ArgIteratorBaseForMethodInvoke |
740 | { |
741 | protected: |
742 | SIGNATURENATIVEREF * m_ppNativeSig; |
743 | |
744 | FORCEINLINE CorElementType GetReturnType(TypeHandle * pthValueType) |
745 | { |
746 | WRAPPER_NO_CONTRACT; |
747 | return (*pthValueType = (*m_ppNativeSig)->GetReturnTypeHandle()).GetInternalCorElementType(); |
748 | } |
749 | |
750 | FORCEINLINE CorElementType GetNextArgumentType(DWORD iArg, TypeHandle * pthValueType) |
751 | { |
752 | WRAPPER_NO_CONTRACT; |
753 | return (*pthValueType = (*m_ppNativeSig)->GetArgumentAt(iArg)).GetInternalCorElementType(); |
754 | } |
755 | |
756 | FORCEINLINE void Reset() |
757 | { |
758 | LIMITED_METHOD_CONTRACT; |
759 | } |
760 | |
761 | FORCEINLINE BOOL IsRegPassedStruct(MethodTable* pMT) |
762 | { |
763 | return pMT->IsRegPassedStruct(); |
764 | } |
765 | |
766 | public: |
767 | BOOL HasThis() |
768 | { |
769 | LIMITED_METHOD_CONTRACT; |
770 | return (*m_ppNativeSig)->HasThis(); |
771 | } |
772 | |
773 | BOOL HasParamType() |
774 | { |
775 | LIMITED_METHOD_CONTRACT; |
776 | // param type methods are not supported for reflection invoke, so HasParamType is always false for them |
777 | return FALSE; |
778 | } |
779 | |
780 | BOOL IsVarArg() |
781 | { |
782 | LIMITED_METHOD_CONTRACT; |
783 | // vararg methods are not supported for reflection invoke, so IsVarArg is always false for them |
784 | return FALSE; |
785 | } |
786 | |
787 | DWORD NumFixedArgs() |
788 | { |
789 | LIMITED_METHOD_CONTRACT; |
790 | return (*m_ppNativeSig)->NumFixedArgs(); |
791 | } |
792 | |
793 | #ifdef FEATURE_INTERPRETER |
794 | BYTE CallConv() |
795 | { |
796 | LIMITED_METHOD_CONTRACT; |
797 | return IMAGE_CEE_CS_CALLCONV_DEFAULT; |
798 | } |
799 | #endif // FEATURE_INTERPRETER |
800 | }; |
801 | |
802 | class ArgIteratorForMethodInvoke : public ArgIteratorTemplate<ArgIteratorBaseForMethodInvoke> |
803 | { |
804 | public: |
805 | ArgIteratorForMethodInvoke(SIGNATURENATIVEREF * ppNativeSig) |
806 | { |
807 | m_ppNativeSig = ppNativeSig; |
808 | |
809 | DWORD dwFlags = (*m_ppNativeSig)->GetArgIteratorFlags(); |
810 | |
811 | // Use the cached values if they are available |
812 | if (dwFlags & SIZE_OF_ARG_STACK_COMPUTED) |
813 | { |
814 | m_dwFlags = dwFlags; |
815 | m_nSizeOfArgStack = (*m_ppNativeSig)->GetSizeOfArgStack(); |
816 | return; |
817 | } |
818 | |
819 | // |
820 | // Compute flags and stack argument size, and cache them for next invocation |
821 | // |
822 | |
823 | ForceSigWalk(); |
824 | |
825 | if (IsActivationNeededForMethodInvoke((*m_ppNativeSig)->GetMethod())) |
826 | { |
827 | m_dwFlags |= METHOD_INVOKE_NEEDS_ACTIVATION; |
828 | } |
829 | |
830 | (*m_ppNativeSig)->SetSizeOfArgStack(m_nSizeOfArgStack); |
831 | _ASSERTE((*m_ppNativeSig)->GetSizeOfArgStack() == m_nSizeOfArgStack); |
832 | |
833 | // This has to be last |
834 | (*m_ppNativeSig)->SetArgIteratorFlags(m_dwFlags); |
835 | _ASSERTE((*m_ppNativeSig)->GetArgIteratorFlags() == m_dwFlags); |
836 | } |
837 | |
838 | BOOL IsActivationNeeded() |
839 | { |
840 | LIMITED_METHOD_CONTRACT; |
841 | return (m_dwFlags & METHOD_INVOKE_NEEDS_ACTIVATION) != 0; |
842 | } |
843 | }; |
844 | |
845 | |
846 | void DECLSPEC_NORETURN ThrowInvokeMethodException(MethodDesc * pMethod, OBJECTREF targetException) |
847 | { |
848 | CONTRACTL { |
849 | THROWS; |
850 | GC_TRIGGERS; |
851 | MODE_COOPERATIVE; |
852 | } |
853 | CONTRACTL_END; |
854 | |
855 | GCPROTECT_BEGIN(targetException); |
856 | |
857 | #if defined(_DEBUG) && !defined(FEATURE_PAL) |
858 | if (IsWatsonEnabled()) |
859 | { |
860 | if (!CLRException::IsPreallocatedExceptionObject(targetException)) |
861 | { |
862 | // If the exception is not preallocated, we should be having the |
863 | // watson buckets in the throwable already. |
864 | if(!((EXCEPTIONREF)targetException)->AreWatsonBucketsPresent()) |
865 | { |
866 | // If an exception is raised by the VM (e.g. type load exception by the JIT) and it comes |
867 | // across the reflection invocation boundary before CLR's personality routine for managed |
868 | // code has been invoked, then no buckets would be available for us at this point. |
869 | // |
870 | // Since we cannot assert this, better log it for diagnosis if required. |
871 | LOG((LF_EH, LL_INFO100, "InvokeImpl - No watson buckets available - regular exception likely raised within VM and not seen by managed code.\n" )); |
872 | } |
873 | } |
874 | else |
875 | { |
876 | // Exception is preallocated. |
877 | PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = GetThread()->GetExceptionState()->GetUEWatsonBucketTracker(); |
878 | if ((IsThrowableThreadAbortException(targetException) && pUEWatsonBucketTracker->CapturedForThreadAbort())|| |
879 | (pUEWatsonBucketTracker->CapturedAtReflectionInvocation())) |
880 | { |
881 | // ReflectionInvocationExceptionFilter would have captured |
882 | // the watson bucket details for preallocated exceptions |
883 | // in the UE watson bucket tracker. |
884 | |
885 | if(pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL) |
886 | { |
887 | // See comment above |
888 | LOG((LF_EH, LL_INFO100, "InvokeImpl - No watson buckets available - preallocated exception likely raised within VM and not seen by managed code.\n" )); |
889 | } |
890 | } |
891 | } |
892 | } |
893 | #endif // _DEBUG && !FEATURE_PAL |
894 | |
895 | #ifdef FEATURE_CORRUPTING_EXCEPTIONS |
896 | // Get the corruption severity of the exception that came in through reflection invocation. |
897 | CorruptionSeverity severity = GetThread()->GetExceptionState()->GetLastActiveExceptionCorruptionSeverity(); |
898 | |
899 | // Since we are dealing with an exception, set the flag indicating if the target of Reflection can handle exception or not. |
900 | // This flag is used in CEHelper::CanIDispatchTargetHandleException. |
901 | GetThread()->GetExceptionState()->SetCanReflectionTargetHandleException(CEHelper::CanMethodHandleException(severity, pMethod)); |
902 | #endif // FEATURE_CORRUPTING_EXCEPTIONS |
903 | |
904 | OBJECTREF except = InvokeUtil::CreateTargetExcept(&targetException); |
905 | |
906 | #ifndef FEATURE_PAL |
907 | if (IsWatsonEnabled()) |
908 | { |
909 | struct |
910 | { |
911 | OBJECTREF oExcept; |
912 | } gcTIE; |
913 | ZeroMemory(&gcTIE, sizeof(gcTIE)); |
914 | GCPROTECT_BEGIN(gcTIE); |
915 | |
916 | gcTIE.oExcept = except; |
917 | |
918 | _ASSERTE(!CLRException::IsPreallocatedExceptionObject(gcTIE.oExcept)); |
919 | |
920 | // If the original exception was preallocated, then copy over the captured |
921 | // watson buckets to the TargetInvocationException object, if available. |
922 | // |
923 | // We dont need to do this if the original exception was not preallocated |
924 | // since it already contains the watson buckets inside the object. |
925 | if (CLRException::IsPreallocatedExceptionObject(targetException)) |
926 | { |
927 | PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = GetThread()->GetExceptionState()->GetUEWatsonBucketTracker(); |
928 | BOOL fCopyWatsonBuckets = TRUE; |
929 | PTR_VOID pBuckets = pUEWatsonBucketTracker->RetrieveWatsonBuckets(); |
930 | if (pBuckets != NULL) |
931 | { |
932 | // Copy the buckets to the exception object |
933 | CopyWatsonBucketsToThrowable(pBuckets, gcTIE.oExcept); |
934 | |
935 | // Confirm that they are present. |
936 | _ASSERTE(((EXCEPTIONREF)gcTIE.oExcept)->AreWatsonBucketsPresent()); |
937 | } |
938 | |
939 | // Clear the UE watson bucket tracker since the bucketing |
940 | // details are now in the TargetInvocationException object. |
941 | pUEWatsonBucketTracker->ClearWatsonBucketDetails(); |
942 | } |
943 | |
944 | // update "except" incase the reference to the object |
945 | // was updated by the GC |
946 | except = gcTIE.oExcept; |
947 | GCPROTECT_END(); |
948 | } |
949 | #endif // !FEATURE_PAL |
950 | |
951 | // Since the original exception is inner of target invocation exception, |
952 | // when TIE is seen to be raised for the first time, we will end up |
953 | // using the inner exception buckets automatically. |
954 | |
955 | // Since VM is throwing the exception, we set it to use the same corruption severity |
956 | // that the original exception came in with from reflection invocation. |
957 | COMPlusThrow(except |
958 | #ifdef FEATURE_CORRUPTING_EXCEPTIONS |
959 | , severity |
960 | #endif // FEATURE_CORRUPTING_EXCEPTIONS |
961 | ); |
962 | |
963 | GCPROTECT_END(); |
964 | } |
965 | |
966 | FCIMPL5(Object*, RuntimeMethodHandle::InvokeMethod, |
967 | Object *target, PTRArray *objs, SignatureNative* pSigUNSAFE, |
968 | CLR_BOOL fConstructor, CLR_BOOL fWrapExceptions) |
969 | { |
970 | FCALL_CONTRACT; |
971 | |
972 | struct { |
973 | OBJECTREF target; |
974 | PTRARRAYREF args; |
975 | SIGNATURENATIVEREF pSig; |
976 | OBJECTREF retVal; |
977 | } gc; |
978 | |
979 | gc.target = ObjectToOBJECTREF(target); |
980 | gc.args = (PTRARRAYREF)objs; |
981 | gc.pSig = (SIGNATURENATIVEREF)pSigUNSAFE; |
982 | gc.retVal = NULL; |
983 | |
984 | MethodDesc* pMeth = gc.pSig->GetMethod(); |
985 | TypeHandle ownerType = gc.pSig->GetDeclaringType(); |
986 | |
987 | HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); |
988 | |
989 | Assembly *pAssem = pMeth->GetAssembly(); |
990 | |
991 | if (ownerType.IsSharedByGenericInstantiations()) |
992 | COMPlusThrow(kNotSupportedException, W("NotSupported_Type" )); |
993 | |
994 | #ifdef _DEBUG |
995 | if (g_pConfig->ShouldInvokeHalt(pMeth)) |
996 | { |
997 | _ASSERTE(!"InvokeHalt" ); |
998 | } |
999 | #endif |
1000 | |
1001 | // Skip the activation optimization for remoting because of remoting proxy is not always activated. |
1002 | // It would be nice to clean this up and get remoting to always activate methodtable behind the proxy. |
1003 | BOOL fForceActivationForRemoting = FALSE; |
1004 | |
1005 | if (fConstructor) |
1006 | { |
1007 | // If we are invoking a constructor on an array then we must |
1008 | // handle this specially. String objects allocate themselves |
1009 | // so they are a special case. |
1010 | if (ownerType.IsArray()) { |
1011 | gc.retVal = InvokeArrayConstructor(ownerType.AsArray(), |
1012 | pMeth, |
1013 | &gc.args, |
1014 | gc.pSig->NumFixedArgs()); |
1015 | goto Done; |
1016 | } |
1017 | |
1018 | MethodTable * pMT = ownerType.AsMethodTable(); |
1019 | |
1020 | { |
1021 | if (pMT != g_pStringClass) |
1022 | gc.retVal = pMT->Allocate(); |
1023 | } |
1024 | } |
1025 | else |
1026 | { |
1027 | } |
1028 | |
1029 | { |
1030 | ArgIteratorForMethodInvoke argit(&gc.pSig); |
1031 | |
1032 | if (argit.IsActivationNeeded() || fForceActivationForRemoting) |
1033 | pMeth->EnsureActive(); |
1034 | CONSISTENCY_CHECK(pMeth->CheckActivated()); |
1035 | |
1036 | UINT nStackBytes = argit.SizeOfFrameArgumentArray(); |
1037 | |
1038 | // Note that SizeOfFrameArgumentArray does overflow checks with sufficient margin to prevent overflows here |
1039 | SIZE_T nAllocaSize = TransitionBlock::GetNegSpaceSize() + sizeof(TransitionBlock) + nStackBytes; |
1040 | |
1041 | Thread * pThread = GET_THREAD(); |
1042 | |
1043 | // Make sure we have enough room on the stack for this. Note that we will need the stack amount twice - once to build the stack |
1044 | // and second time to actually make the call. |
1045 | INTERIOR_STACK_PROBE_FOR(pThread, 1 + static_cast<UINT>((2 * nAllocaSize) / GetOsPageSize()) + static_cast<UINT>(HOLDER_CODE_NORMAL_STACK_LIMIT)); |
1046 | |
1047 | LPBYTE pAlloc = (LPBYTE)_alloca(nAllocaSize); |
1048 | |
1049 | LPBYTE pTransitionBlock = pAlloc + TransitionBlock::GetNegSpaceSize(); |
1050 | |
1051 | CallDescrData callDescrData; |
1052 | |
1053 | callDescrData.pSrc = pTransitionBlock + sizeof(TransitionBlock); |
1054 | callDescrData.numStackSlots = nStackBytes / STACK_ELEM_SIZE; |
1055 | #ifdef CALLDESCR_ARGREGS |
1056 | callDescrData.pArgumentRegisters = (ArgumentRegisters*)(pTransitionBlock + TransitionBlock::GetOffsetOfArgumentRegisters()); |
1057 | #endif |
1058 | #ifdef CALLDESCR_RETBUFFARGREG |
1059 | callDescrData.pRetBuffArg = (UINT64*)(pTransitionBlock + TransitionBlock::GetOffsetOfRetBuffArgReg()); |
1060 | #endif |
1061 | #ifdef CALLDESCR_FPARGREGS |
1062 | callDescrData.pFloatArgumentRegisters = NULL; |
1063 | #endif |
1064 | #ifdef CALLDESCR_REGTYPEMAP |
1065 | callDescrData.dwRegTypeMap = 0; |
1066 | #endif |
1067 | callDescrData.fpReturnSize = argit.GetFPReturnSize(); |
1068 | |
1069 | // This is duplicated logic from MethodDesc::GetCallTarget |
1070 | PCODE pTarget; |
1071 | if (pMeth->IsVtableMethod()) |
1072 | { |
1073 | pTarget = pMeth->GetSingleCallableAddrOfVirtualizedCode(&gc.target, ownerType); |
1074 | } |
1075 | else |
1076 | { |
1077 | pTarget = pMeth->GetSingleCallableAddrOfCode(); |
1078 | } |
1079 | callDescrData.pTarget = pTarget; |
1080 | |
1081 | // Build the arguments on the stack |
1082 | |
1083 | GCStress<cfg_any>::MaybeTrigger(); |
1084 | |
1085 | FrameWithCookie<ProtectValueClassFrame> *pProtectValueClassFrame = NULL; |
1086 | ValueClassInfo *pValueClasses = NULL; |
1087 | ByRefToNullable* byRefToNullables = NULL; |
1088 | |
1089 | // if we have the magic Value Class return, we need to allocate that class |
1090 | // and place a pointer to it on the stack. |
1091 | |
1092 | BOOL hasRefReturnAndNeedsBoxing = FALSE; // Indicates that the method has a BYREF return type and the target type needs to be copied into a preallocated boxed object. |
1093 | |
1094 | TypeHandle retTH = gc.pSig->GetReturnTypeHandle(); |
1095 | |
1096 | TypeHandle refReturnTargetTH; // Valid only if retType == ELEMENT_TYPE_BYREF. Caches the TypeHandle of the byref target. |
1097 | BOOL fHasRetBuffArg = argit.HasRetBuffArg(); |
1098 | CorElementType retType = retTH.GetSignatureCorElementType(); |
1099 | BOOL hasValueTypeReturn = retTH.IsValueType() && retType != ELEMENT_TYPE_VOID; |
1100 | _ASSERTE(hasValueTypeReturn || !fHasRetBuffArg); // only valuetypes are returned via a return buffer. |
1101 | if (hasValueTypeReturn) { |
1102 | gc.retVal = retTH.GetMethodTable()->Allocate(); |
1103 | } |
1104 | else if (retType == ELEMENT_TYPE_BYREF) |
1105 | { |
1106 | refReturnTargetTH = retTH.AsTypeDesc()->GetTypeParam(); |
1107 | |
1108 | // If the target of the byref is a value type, we need to preallocate a boxed object to hold the managed return value. |
1109 | if (refReturnTargetTH.IsValueType()) |
1110 | { |
1111 | _ASSERTE(refReturnTargetTH.GetSignatureCorElementType() != ELEMENT_TYPE_VOID); // Managed Reflection layer has a bouncer for "ref void" returns. |
1112 | hasRefReturnAndNeedsBoxing = TRUE; |
1113 | gc.retVal = refReturnTargetTH.GetMethodTable()->Allocate(); |
1114 | } |
1115 | } |
1116 | |
1117 | // Copy "this" pointer |
1118 | if (!pMeth->IsStatic()) { |
1119 | PVOID pThisPtr; |
1120 | |
1121 | if (fConstructor) |
1122 | { |
1123 | // Copy "this" pointer: only unbox if type is value type and method is not unboxing stub |
1124 | if (ownerType.IsValueType() && !pMeth->IsUnboxingStub()) { |
1125 | // Note that we create a true boxed nullabe<T> and then convert it to a T below |
1126 | pThisPtr = gc.retVal->GetData(); |
1127 | } |
1128 | else |
1129 | pThisPtr = OBJECTREFToObject(gc.retVal); |
1130 | } |
1131 | else |
1132 | if (!pMeth->GetMethodTable()->IsValueType()) |
1133 | pThisPtr = OBJECTREFToObject(gc.target); |
1134 | else { |
1135 | if (pMeth->IsUnboxingStub()) |
1136 | pThisPtr = OBJECTREFToObject(gc.target); |
1137 | else { |
1138 | // Create a true boxed Nullable<T> and use that as the 'this' pointer. |
1139 | // since what is passed in is just a boxed T |
1140 | MethodTable* pMT = pMeth->GetMethodTable(); |
1141 | if (Nullable::IsNullableType(pMT)) { |
1142 | OBJECTREF bufferObj = pMT->Allocate(); |
1143 | void* buffer = bufferObj->GetData(); |
1144 | Nullable::UnBox(buffer, gc.target, pMT); |
1145 | pThisPtr = buffer; |
1146 | } |
1147 | else |
1148 | pThisPtr = gc.target->UnBox(); |
1149 | } |
1150 | } |
1151 | |
1152 | *((LPVOID*) (pTransitionBlock + argit.GetThisOffset())) = pThisPtr; |
1153 | } |
1154 | |
1155 | // NO GC AFTER THIS POINT. The object references in the method frame are not protected. |
1156 | // |
1157 | // We have already copied "this" pointer so we do not want GC to happen even sooner. Unfortunately, |
1158 | // we may allocate in the process of copying this pointer that makes it hard to express using contracts. |
1159 | // |
1160 | // If an exception occurs a gc may happen but we are going to dump the stack anyway and we do |
1161 | // not need to protect anything. |
1162 | |
1163 | PVOID pRetBufStackCopy = NULL; |
1164 | |
1165 | { |
1166 | BEGINFORBIDGC(); |
1167 | #ifdef _DEBUG |
1168 | GCForbidLoaderUseHolder forbidLoaderUse; |
1169 | #endif |
1170 | |
1171 | // Take care of any return arguments |
1172 | if (fHasRetBuffArg) |
1173 | { |
1174 | // We stack-allocate this ret buff, to preserve the invariant that ret-buffs are always in the |
1175 | // caller's stack frame. We'll copy into gc.retVal later. |
1176 | TypeHandle retTH = gc.pSig->GetReturnTypeHandle(); |
1177 | MethodTable* pMT = retTH.GetMethodTable(); |
1178 | if (pMT->IsStructRequiringStackAllocRetBuf()) |
1179 | { |
1180 | SIZE_T sz = pMT->GetNumInstanceFieldBytes(); |
1181 | pRetBufStackCopy = _alloca(sz); |
1182 | memset(pRetBufStackCopy, 0, sz); |
1183 | |
1184 | pValueClasses = new (_alloca(sizeof(ValueClassInfo))) ValueClassInfo(pRetBufStackCopy, pMT, pValueClasses); |
1185 | *((LPVOID*) (pTransitionBlock + argit.GetRetBuffArgOffset())) = pRetBufStackCopy; |
1186 | } |
1187 | else |
1188 | { |
1189 | PVOID pRetBuff = gc.retVal->GetData(); |
1190 | *((LPVOID*) (pTransitionBlock + argit.GetRetBuffArgOffset())) = pRetBuff; |
1191 | } |
1192 | } |
1193 | |
1194 | // copy args |
1195 | UINT nNumArgs = gc.pSig->NumFixedArgs(); |
1196 | for (UINT i = 0 ; i < nNumArgs; i++) { |
1197 | |
1198 | TypeHandle th = gc.pSig->GetArgumentAt(i); |
1199 | |
1200 | int ofs = argit.GetNextOffset(); |
1201 | _ASSERTE(ofs != TransitionBlock::InvalidOffset); |
1202 | |
1203 | #ifdef CALLDESCR_REGTYPEMAP |
1204 | FillInRegTypeMap(ofs, argit.GetArgType(), (BYTE *)&callDescrData.dwRegTypeMap); |
1205 | #endif |
1206 | |
1207 | #ifdef CALLDESCR_FPARGREGS |
1208 | // Under CALLDESCR_FPARGREGS -ve offsets indicate arguments in floating point registers. If we have at |
1209 | // least one such argument we point the call worker at the floating point area of the frame (we leave |
1210 | // it null otherwise since the worker can perform a useful optimization if it knows no floating point |
1211 | // registers need to be set up). |
1212 | |
1213 | if (TransitionBlock::HasFloatRegister(ofs, argit.GetArgLocDescForStructInRegs()) && |
1214 | (callDescrData.pFloatArgumentRegisters == NULL)) |
1215 | { |
1216 | callDescrData.pFloatArgumentRegisters = (FloatArgumentRegisters*) (pTransitionBlock + |
1217 | TransitionBlock::GetOffsetOfFloatArgumentRegisters()); |
1218 | } |
1219 | #endif |
1220 | |
1221 | UINT structSize = argit.GetArgSize(); |
1222 | |
1223 | bool needsStackCopy = false; |
1224 | |
1225 | // A boxed Nullable<T> is represented as boxed T. So to pass a Nullable<T> by reference, |
1226 | // we have to create a Nullable<T> on stack, copy the T into it, then pass it to the callee and |
1227 | // after returning from the call, copy the T out of the Nullable<T> back to the boxed T. |
1228 | TypeHandle nullableType = NullableTypeOfByref(th); |
1229 | if (!nullableType.IsNull()) { |
1230 | th = nullableType; |
1231 | structSize = th.GetSize(); |
1232 | needsStackCopy = true; |
1233 | } |
1234 | #ifdef ENREGISTERED_PARAMTYPE_MAXSIZE |
1235 | else if (argit.IsArgPassedByRef()) |
1236 | { |
1237 | needsStackCopy = true; |
1238 | } |
1239 | #endif |
1240 | |
1241 | ArgDestination argDest(pTransitionBlock, ofs, argit.GetArgLocDescForStructInRegs()); |
1242 | |
1243 | if(needsStackCopy) |
1244 | { |
1245 | MethodTable * pMT = th.GetMethodTable(); |
1246 | _ASSERTE(pMT && pMT->IsValueType()); |
1247 | |
1248 | PVOID pArgDst = argDest.GetDestinationAddress(); |
1249 | |
1250 | PVOID pStackCopy = _alloca(structSize); |
1251 | *(PVOID *)pArgDst = pStackCopy; |
1252 | pArgDst = pStackCopy; |
1253 | |
1254 | if (!nullableType.IsNull()) |
1255 | { |
1256 | byRefToNullables = new(_alloca(sizeof(ByRefToNullable))) ByRefToNullable(i, pStackCopy, nullableType, byRefToNullables); |
1257 | } |
1258 | |
1259 | // save the info into ValueClassInfo |
1260 | if (pMT->ContainsPointers()) |
1261 | { |
1262 | pValueClasses = new (_alloca(sizeof(ValueClassInfo))) ValueClassInfo(pStackCopy, pMT, pValueClasses); |
1263 | } |
1264 | |
1265 | // We need a new ArgDestination that points to the stack copy |
1266 | argDest = ArgDestination(pStackCopy, 0, NULL); |
1267 | } |
1268 | |
1269 | InvokeUtil::CopyArg(th, &(gc.args->m_Array[i]), &argDest); |
1270 | } |
1271 | |
1272 | ENDFORBIDGC(); |
1273 | } |
1274 | |
1275 | #ifdef FEATURE_CORRUPTING_EXCEPTIONS |
1276 | // By default, set the flag in TES indicating the reflection target can handle CSE. |
1277 | // This flag is used in CEHelper::CanIDispatchTargetHandleException. |
1278 | pThread->GetExceptionState()->SetCanReflectionTargetHandleException(TRUE); |
1279 | #endif // FEATURE_CORRUPTING_EXCEPTIONS |
1280 | |
1281 | if (pValueClasses != NULL) |
1282 | { |
1283 | pProtectValueClassFrame = new (_alloca (sizeof (FrameWithCookie<ProtectValueClassFrame>))) |
1284 | FrameWithCookie<ProtectValueClassFrame>(pThread, pValueClasses); |
1285 | } |
1286 | |
1287 | // Call the method |
1288 | bool fExceptionThrown = false; |
1289 | if (fWrapExceptions) |
1290 | { |
1291 | // The sole purpose of having this frame is to tell the debugger that we have a catch handler here |
1292 | // which may swallow managed exceptions. The debugger needs this in order to send a |
1293 | // CatchHandlerFound (CHF) notification. |
1294 | FrameWithCookie<DebuggerU2MCatchHandlerFrame> catchFrame(pThread); |
1295 | |
1296 | EX_TRY_THREAD(pThread) { |
1297 | CallDescrWorkerReflectionWrapper(&callDescrData, &catchFrame); |
1298 | } EX_CATCH{ |
1299 | // Rethrow transient exceptions for constructors for backward compatibility |
1300 | if (fConstructor && GET_EXCEPTION()->IsTransient()) |
1301 | { |
1302 | EX_RETHROW; |
1303 | } |
1304 | |
1305 | // Abuse retval to store the exception object |
1306 | gc.retVal = GET_THROWABLE(); |
1307 | _ASSERTE(gc.retVal); |
1308 | |
1309 | fExceptionThrown = true; |
1310 | } EX_END_CATCH(SwallowAllExceptions); |
1311 | |
1312 | catchFrame.Pop(pThread); |
1313 | } |
1314 | else |
1315 | { |
1316 | CallDescrWorkerWithHandler(&callDescrData); |
1317 | } |
1318 | |
1319 | |
1320 | // Now that we are safely out of the catch block, we can create and raise the |
1321 | // TargetInvocationException. |
1322 | if (fExceptionThrown) |
1323 | { |
1324 | ThrowInvokeMethodException(pMeth, gc.retVal); |
1325 | } |
1326 | |
1327 | // It is still illegal to do a GC here. The return type might have/contain GC pointers. |
1328 | if (fConstructor) |
1329 | { |
1330 | // We have a special case for Strings...The object is returned... |
1331 | if (ownerType == TypeHandle(g_pStringClass)) { |
1332 | PVOID pReturnValue = &callDescrData.returnValue; |
1333 | gc.retVal = *(OBJECTREF *)pReturnValue; |
1334 | } |
1335 | |
1336 | // If it is a Nullable<T>, box it using Nullable<T> conventions. |
1337 | // TODO: this double allocates on constructions which is wasteful |
1338 | gc.retVal = Nullable::NormalizeBox(gc.retVal); |
1339 | } |
1340 | else |
1341 | if (hasValueTypeReturn || hasRefReturnAndNeedsBoxing) |
1342 | { |
1343 | _ASSERTE(gc.retVal != NULL); |
1344 | |
1345 | if (hasRefReturnAndNeedsBoxing) |
1346 | { |
1347 | // Method has BYREF return and the target type is one that needs boxing. We need to copy into the boxed object we have allocated for this purpose. |
1348 | LPVOID pReturnedReference = *(LPVOID*)&callDescrData.returnValue; |
1349 | if (pReturnedReference == NULL) |
1350 | { |
1351 | COMPlusThrow(kNullReferenceException, IDS_INVOKE_NULLREF_RETURNED); |
1352 | } |
1353 | CopyValueClass(gc.retVal->GetData(), pReturnedReference, gc.retVal->GetMethodTable(), gc.retVal->GetAppDomain()); |
1354 | } |
1355 | // if the structure is returned by value, then we need to copy in the boxed object |
1356 | // we have allocated for this purpose. |
1357 | else if (!fHasRetBuffArg) |
1358 | { |
1359 | CopyValueClass(gc.retVal->GetData(), &callDescrData.returnValue, gc.retVal->GetMethodTable(), gc.retVal->GetAppDomain()); |
1360 | } |
1361 | else if (pRetBufStackCopy) |
1362 | { |
1363 | CopyValueClass(gc.retVal->GetData(), pRetBufStackCopy, gc.retVal->GetMethodTable(), gc.retVal->GetAppDomain()); |
1364 | } |
1365 | // From here on out, it is OK to have GCs since the return object (which may have had |
1366 | // GC pointers has been put into a GC object and thus protected. |
1367 | |
1368 | // TODO this creates two objects which is inefficient |
1369 | // If the return type is a Nullable<T> box it into the correct form |
1370 | gc.retVal = Nullable::NormalizeBox(gc.retVal); |
1371 | } |
1372 | else if (retType == ELEMENT_TYPE_BYREF) |
1373 | { |
1374 | // WARNING: pReturnedReference is an unprotected inner reference so we must not trigger a GC until the referenced value has been safely captured. |
1375 | LPVOID pReturnedReference = *(LPVOID*)&callDescrData.returnValue; |
1376 | if (pReturnedReference == NULL) |
1377 | { |
1378 | COMPlusThrow(kNullReferenceException, IDS_INVOKE_NULLREF_RETURNED); |
1379 | } |
1380 | |
1381 | gc.retVal = InvokeUtil::CreateObjectAfterInvoke(refReturnTargetTH, pReturnedReference); |
1382 | } |
1383 | else |
1384 | { |
1385 | gc.retVal = InvokeUtil::CreateObjectAfterInvoke(retTH, &callDescrData.returnValue); |
1386 | } |
1387 | |
1388 | while (byRefToNullables != NULL) { |
1389 | OBJECTREF obj = Nullable::Box(byRefToNullables->data, byRefToNullables->type.GetMethodTable()); |
1390 | SetObjectReference(&gc.args->m_Array[byRefToNullables->argNum], obj, gc.args->GetAppDomain()); |
1391 | byRefToNullables = byRefToNullables->next; |
1392 | } |
1393 | |
1394 | if (pProtectValueClassFrame != NULL) |
1395 | pProtectValueClassFrame->Pop(pThread); |
1396 | |
1397 | END_INTERIOR_STACK_PROBE; |
1398 | } |
1399 | |
1400 | Done: |
1401 | HELPER_METHOD_FRAME_END(); |
1402 | |
1403 | return OBJECTREFToObject(gc.retVal); |
1404 | } |
1405 | FCIMPLEND |
1406 | |
1407 | struct SkipStruct { |
1408 | StackCrawlMark* pStackMark; |
1409 | MethodDesc* pMeth; |
1410 | }; |
1411 | |
1412 | // This method is called by the GetMethod function and will crawl backward |
1413 | // up the stack for integer methods. |
1414 | static StackWalkAction SkipMethods(CrawlFrame* frame, VOID* data) { |
1415 | CONTRACTL { |
1416 | NOTHROW; |
1417 | GC_NOTRIGGER; |
1418 | MODE_ANY; |
1419 | } |
1420 | CONTRACTL_END; |
1421 | |
1422 | SkipStruct* pSkip = (SkipStruct*) data; |
1423 | |
1424 | MethodDesc *pFunc = frame->GetFunction(); |
1425 | |
1426 | /* We asked to be called back only for functions */ |
1427 | _ASSERTE(pFunc); |
1428 | |
1429 | // The check here is between the address of a local variable |
1430 | // (the stack mark) and a pointer to the EIP for a frame |
1431 | // (which is actually the pointer to the return address to the |
1432 | // function from the previous frame). So we'll actually notice |
1433 | // which frame the stack mark was in one frame later. This is |
1434 | // fine since we only implement LookForMyCaller. |
1435 | _ASSERTE(*pSkip->pStackMark == LookForMyCaller); |
1436 | if (!frame->IsInCalleesFrames(pSkip->pStackMark)) |
1437 | return SWA_CONTINUE; |
1438 | |
1439 | if (pFunc->RequiresInstMethodDescArg()) |
1440 | { |
1441 | pSkip->pMeth = (MethodDesc *) frame->GetParamTypeArg(); |
1442 | if (pSkip->pMeth == NULL) |
1443 | pSkip->pMeth = pFunc; |
1444 | } |
1445 | else |
1446 | pSkip->pMeth = pFunc; |
1447 | return SWA_ABORT; |
1448 | } |
1449 | |
1450 | // Return the MethodInfo that represents the current method (two above this one) |
1451 | FCIMPL1(ReflectMethodObject*, RuntimeMethodHandle::GetCurrentMethod, StackCrawlMark* stackMark) { |
1452 | FCALL_CONTRACT; |
1453 | REFLECTMETHODREF pRet = NULL; |
1454 | |
1455 | HELPER_METHOD_FRAME_BEGIN_RET_0(); |
1456 | SkipStruct skip; |
1457 | skip.pStackMark = stackMark; |
1458 | skip.pMeth = 0; |
1459 | StackWalkFunctions(GetThread(), SkipMethods, &skip); |
1460 | |
1461 | // If C<Foo>.m<Bar> was called, the stack walker returns C<object>.m<object>. We cannot |
1462 | // get know that the instantiation used Foo or Bar at that point. So the next best thing |
1463 | // is to return C<T>.m<P> and that's what LoadTypicalMethodDefinition will do for us. |
1464 | |
1465 | if (skip.pMeth != NULL) |
1466 | pRet = skip.pMeth->LoadTypicalMethodDefinition()->GetStubMethodInfo(); |
1467 | else |
1468 | pRet = NULL; |
1469 | |
1470 | HELPER_METHOD_FRAME_END(); |
1471 | |
1472 | return (ReflectMethodObject*)OBJECTREFToObject(pRet); |
1473 | } |
1474 | FCIMPLEND |
1475 | |
1476 | static OBJECTREF DirectObjectFieldGet(FieldDesc *pField, TypeHandle fieldType, TypeHandle enclosingType, TypedByRef *pTarget, CLR_BOOL *pDomainInitialized) { |
1477 | CONTRACTL { |
1478 | THROWS; |
1479 | GC_TRIGGERS; |
1480 | MODE_COOPERATIVE; |
1481 | |
1482 | PRECONDITION(CheckPointer(pField)); |
1483 | } |
1484 | CONTRACTL_END; |
1485 | |
1486 | OBJECTREF refRet; |
1487 | OBJECTREF objref = NULL; |
1488 | GCPROTECT_BEGIN(objref); |
1489 | if (!pField->IsStatic()) { |
1490 | objref = ObjectToOBJECTREF(*((Object**)pTarget->data)); |
1491 | } |
1492 | |
1493 | InvokeUtil::ValidateObjectTarget(pField, enclosingType, &objref); |
1494 | refRet = InvokeUtil::GetFieldValue(pField, fieldType, &objref, enclosingType, pDomainInitialized); |
1495 | GCPROTECT_END(); |
1496 | return refRet; |
1497 | } |
1498 | |
1499 | FCIMPL4(Object*, RuntimeFieldHandle::GetValueDirect, ReflectFieldObject *pFieldUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, TypedByRef *pTarget, ReflectClassBaseObject *pDeclaringTypeUNSAFE) { |
1500 | CONTRACTL { |
1501 | FCALL_CHECK; |
1502 | } |
1503 | CONTRACTL_END; |
1504 | |
1505 | struct |
1506 | { |
1507 | REFLECTCLASSBASEREF refFieldType; |
1508 | REFLECTCLASSBASEREF refDeclaringType; |
1509 | REFLECTFIELDREF refField; |
1510 | }gc; |
1511 | gc.refFieldType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pFieldTypeUNSAFE); |
1512 | gc.refDeclaringType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pDeclaringTypeUNSAFE); |
1513 | gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(pFieldUNSAFE); |
1514 | |
1515 | if ((gc.refFieldType == NULL) || (gc.refField == NULL)) |
1516 | FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle" )); |
1517 | |
1518 | TypeHandle fieldType = gc.refFieldType->GetType(); |
1519 | |
1520 | FieldDesc *pField = gc.refField->GetField(); |
1521 | |
1522 | Assembly *pAssem = pField->GetModule()->GetAssembly(); |
1523 | |
1524 | OBJECTREF refRet = NULL; |
1525 | CorElementType fieldElType; |
1526 | |
1527 | HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); |
1528 | |
1529 | // Find the Object and its type |
1530 | TypeHandle targetType = pTarget->type; |
1531 | _ASSERTE(gc.refDeclaringType == NULL || !gc.refDeclaringType->GetType().IsTypeDesc()); |
1532 | MethodTable *pEnclosingMT = (gc.refDeclaringType != NULL ? gc.refDeclaringType->GetType() : TypeHandle()).AsMethodTable(); |
1533 | |
1534 | // Verify the callee/caller access |
1535 | if (!pField->IsPublic() || (pEnclosingMT != NULL && !pEnclosingMT->IsExternallyVisible())) |
1536 | { |
1537 | |
1538 | bool targetRemoted = false; |
1539 | |
1540 | |
1541 | RefSecContext sCtx(InvokeUtil::GetInvocationAccessCheckType(targetRemoted)); |
1542 | |
1543 | MethodTable* pInstanceMT = NULL; |
1544 | if (!pField->IsStatic()) |
1545 | { |
1546 | if (!targetType.IsTypeDesc()) |
1547 | pInstanceMT = targetType.AsMethodTable(); |
1548 | } |
1549 | |
1550 | //TODO: missing check that the field is consistent |
1551 | |
1552 | // Perform the normal access check (caller vs field). |
1553 | InvokeUtil::CanAccessField(&sCtx, |
1554 | pEnclosingMT, |
1555 | pInstanceMT, |
1556 | pField); |
1557 | } |
1558 | |
1559 | CLR_BOOL domainInitialized = FALSE; |
1560 | if (pField->IsStatic() || !targetType.IsValueType()) { |
1561 | refRet = DirectObjectFieldGet(pField, fieldType, TypeHandle(pEnclosingMT), pTarget, &domainInitialized); |
1562 | goto lExit; |
1563 | } |
1564 | |
1565 | // Validate that the target type can be cast to the type that owns this field info. |
1566 | if (!targetType.CanCastTo(TypeHandle(pEnclosingMT))) |
1567 | COMPlusThrowArgumentException(W("obj" ), NULL); |
1568 | |
1569 | // This is a workaround because from the previous case we may end up with an |
1570 | // Enum. We want to process it here. |
1571 | // Get the value from the field |
1572 | void* p; |
1573 | fieldElType = fieldType.GetSignatureCorElementType(); |
1574 | switch (fieldElType) { |
1575 | case ELEMENT_TYPE_VOID: |
1576 | _ASSERTE(!"Void used as Field Type!" ); |
1577 | COMPlusThrow(kInvalidProgramException); |
1578 | |
1579 | case ELEMENT_TYPE_BOOLEAN: // boolean |
1580 | case ELEMENT_TYPE_I1: // byte |
1581 | case ELEMENT_TYPE_U1: // unsigned byte |
1582 | case ELEMENT_TYPE_I2: // short |
1583 | case ELEMENT_TYPE_U2: // unsigned short |
1584 | case ELEMENT_TYPE_CHAR: // char |
1585 | case ELEMENT_TYPE_I4: // int |
1586 | case ELEMENT_TYPE_U4: // unsigned int |
1587 | case ELEMENT_TYPE_I: |
1588 | case ELEMENT_TYPE_U: |
1589 | case ELEMENT_TYPE_R4: // float |
1590 | case ELEMENT_TYPE_I8: // long |
1591 | case ELEMENT_TYPE_U8: // unsigned long |
1592 | case ELEMENT_TYPE_R8: // double |
1593 | case ELEMENT_TYPE_VALUETYPE: |
1594 | _ASSERTE(!fieldType.IsTypeDesc()); |
1595 | p = ((BYTE*) pTarget->data) + pField->GetOffset(); |
1596 | refRet = fieldType.AsMethodTable()->Box(p); |
1597 | break; |
1598 | |
1599 | case ELEMENT_TYPE_OBJECT: |
1600 | case ELEMENT_TYPE_CLASS: |
1601 | case ELEMENT_TYPE_SZARRAY: // Single Dim, Zero |
1602 | case ELEMENT_TYPE_ARRAY: // general array |
1603 | p = ((BYTE*) pTarget->data) + pField->GetOffset(); |
1604 | refRet = ObjectToOBJECTREF(*(Object**) p); |
1605 | break; |
1606 | |
1607 | case ELEMENT_TYPE_PTR: |
1608 | { |
1609 | p = ((BYTE*) pTarget->data) + pField->GetOffset(); |
1610 | |
1611 | refRet = InvokeUtil::CreatePointer(fieldType, *(void **)p); |
1612 | |
1613 | break; |
1614 | } |
1615 | |
1616 | default: |
1617 | _ASSERTE(!"Unknown Type" ); |
1618 | // this is really an impossible condition |
1619 | COMPlusThrow(kNotSupportedException); |
1620 | } |
1621 | |
1622 | lExit: ; |
1623 | HELPER_METHOD_FRAME_END(); |
1624 | return OBJECTREFToObject(refRet); |
1625 | } |
1626 | FCIMPLEND |
1627 | |
1628 | static void DirectObjectFieldSet(FieldDesc *pField, TypeHandle fieldType, TypeHandle enclosingType, TypedByRef *pTarget, OBJECTREF *pValue, CLR_BOOL *pDomainInitialized) { |
1629 | CONTRACTL { |
1630 | THROWS; |
1631 | GC_TRIGGERS; |
1632 | MODE_COOPERATIVE; |
1633 | |
1634 | PRECONDITION(CheckPointer(pField)); |
1635 | PRECONDITION(!fieldType.IsNull()); |
1636 | } |
1637 | CONTRACTL_END; |
1638 | |
1639 | OBJECTREF objref = NULL; |
1640 | GCPROTECT_BEGIN(objref); |
1641 | if (!pField->IsStatic()) { |
1642 | objref = ObjectToOBJECTREF(*((Object**)pTarget->data)); |
1643 | } |
1644 | // Validate the target/fld type relationship |
1645 | InvokeUtil::ValidateObjectTarget(pField, enclosingType, &objref); |
1646 | |
1647 | InvokeUtil::ValidField(fieldType, pValue); |
1648 | InvokeUtil::SetValidField(pField->GetFieldType(), fieldType, pField, &objref, pValue, enclosingType, pDomainInitialized); |
1649 | GCPROTECT_END(); |
1650 | } |
1651 | |
1652 | FCIMPL5(void, RuntimeFieldHandle::SetValueDirect, ReflectFieldObject *pFieldUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE, TypedByRef *pTarget, Object *valueUNSAFE, ReflectClassBaseObject *pContextTypeUNSAFE) { |
1653 | CONTRACTL { |
1654 | FCALL_CHECK; |
1655 | } |
1656 | CONTRACTL_END; |
1657 | |
1658 | struct _gc |
1659 | { |
1660 | OBJECTREF oValue; |
1661 | REFLECTCLASSBASEREF pFieldType; |
1662 | REFLECTCLASSBASEREF pContextType; |
1663 | REFLECTFIELDREF refField; |
1664 | }gc; |
1665 | |
1666 | gc.oValue = ObjectToOBJECTREF(valueUNSAFE); |
1667 | gc.pFieldType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pFieldTypeUNSAFE); |
1668 | gc.pContextType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pContextTypeUNSAFE); |
1669 | gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(pFieldUNSAFE); |
1670 | |
1671 | if ((gc.pFieldType == NULL) || (gc.refField == NULL)) |
1672 | FCThrowResVoid(kArgumentNullException, W("Arg_InvalidHandle" )); |
1673 | |
1674 | TypeHandle fieldType = gc.pFieldType->GetType(); |
1675 | TypeHandle contextType = (gc.pContextType != NULL) ? gc.pContextType->GetType() : NULL; |
1676 | |
1677 | FieldDesc *pField = gc.refField->GetField(); |
1678 | |
1679 | Assembly *pAssem = pField->GetModule()->GetAssembly(); |
1680 | |
1681 | BYTE *pDst = NULL; |
1682 | ARG_SLOT value = NULL; |
1683 | CorElementType fieldElType; |
1684 | |
1685 | HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); |
1686 | |
1687 | // Find the Object and its type |
1688 | TypeHandle targetType = pTarget->type; |
1689 | MethodTable *pEnclosingMT = contextType.GetMethodTable(); |
1690 | |
1691 | { |
1692 | // Verify that the value passed can be widened into the target |
1693 | InvokeUtil::ValidField(fieldType, &gc.oValue); |
1694 | |
1695 | // Verify we're not trying to set the value of a static initonly field |
1696 | // once the class has been initialized. |
1697 | if (pField->IsStatic() && pEnclosingMT->IsClassInited() && IsFdInitOnly(pField->GetAttributes())) |
1698 | { |
1699 | DefineFullyQualifiedNameForClassW(); |
1700 | SString ssFieldName(SString::Utf8, pField->GetName()); |
1701 | COMPlusThrow(kFieldAccessException, |
1702 | IDS_EE_CANNOT_SET_INITONLY_STATIC_FIELD, |
1703 | ssFieldName.GetUnicode(), |
1704 | GetFullyQualifiedNameForClassW(pEnclosingMT)); |
1705 | } |
1706 | |
1707 | // Verify the callee/caller access |
1708 | if (!pField->IsPublic() || (pEnclosingMT != NULL && !pEnclosingMT->IsExternallyVisible())) |
1709 | { |
1710 | // security and consistency checks |
1711 | |
1712 | bool targetRemoted = false; |
1713 | |
1714 | RefSecContext sCtx(InvokeUtil::GetInvocationAccessCheckType(targetRemoted)); |
1715 | |
1716 | MethodTable* pInstanceMT = NULL; |
1717 | if (!pField->IsStatic()) { |
1718 | if (!targetType.IsTypeDesc()) |
1719 | pInstanceMT = targetType.AsMethodTable(); |
1720 | } |
1721 | |
1722 | //TODO: missing check that the field is consistent |
1723 | |
1724 | // Perform the normal access check (caller vs field). |
1725 | InvokeUtil::CanAccessField(&sCtx, |
1726 | pEnclosingMT, |
1727 | pInstanceMT, |
1728 | pField); |
1729 | } |
1730 | |
1731 | } |
1732 | |
1733 | CLR_BOOL domainInitialized = FALSE; |
1734 | if (pField->IsStatic() || !targetType.IsValueType()) { |
1735 | DirectObjectFieldSet(pField, fieldType, TypeHandle(pEnclosingMT), pTarget, &gc.oValue, &domainInitialized); |
1736 | goto lExit; |
1737 | } |
1738 | |
1739 | if (gc.oValue == NULL && fieldType.IsValueType() && !Nullable::IsNullableType(fieldType)) |
1740 | COMPlusThrowArgumentNull(W("value" )); |
1741 | |
1742 | // Validate that the target type can be cast to the type that owns this field info. |
1743 | if (!targetType.CanCastTo(TypeHandle(pEnclosingMT))) |
1744 | COMPlusThrowArgumentException(W("obj" ), NULL); |
1745 | |
1746 | // Set the field |
1747 | fieldElType = fieldType.GetInternalCorElementType(); |
1748 | if (ELEMENT_TYPE_BOOLEAN <= fieldElType && fieldElType <= ELEMENT_TYPE_R8) { |
1749 | CorElementType objType = gc.oValue->GetTypeHandle().GetInternalCorElementType(); |
1750 | if (objType != fieldElType) |
1751 | InvokeUtil::CreatePrimitiveValue(fieldElType, objType, gc.oValue, &value); |
1752 | else |
1753 | value = *(ARG_SLOT*)gc.oValue->UnBox(); |
1754 | } |
1755 | pDst = ((BYTE*) pTarget->data) + pField->GetOffset(); |
1756 | |
1757 | switch (fieldElType) { |
1758 | case ELEMENT_TYPE_VOID: |
1759 | _ASSERTE(!"Void used as Field Type!" ); |
1760 | COMPlusThrow(kInvalidProgramException); |
1761 | |
1762 | case ELEMENT_TYPE_BOOLEAN: // boolean |
1763 | case ELEMENT_TYPE_I1: // byte |
1764 | case ELEMENT_TYPE_U1: // unsigned byte |
1765 | VolatileStore((UINT8*)pDst, *(UINT8*)&value); |
1766 | break; |
1767 | |
1768 | case ELEMENT_TYPE_I2: // short |
1769 | case ELEMENT_TYPE_U2: // unsigned short |
1770 | case ELEMENT_TYPE_CHAR: // char |
1771 | VolatileStore((UINT16*)pDst, *(UINT16*)&value); |
1772 | break; |
1773 | |
1774 | case ELEMENT_TYPE_I4: // int |
1775 | case ELEMENT_TYPE_U4: // unsigned int |
1776 | case ELEMENT_TYPE_R4: // float |
1777 | VolatileStore((UINT32*)pDst, *(UINT32*)&value); |
1778 | break; |
1779 | |
1780 | case ELEMENT_TYPE_I8: // long |
1781 | case ELEMENT_TYPE_U8: // unsigned long |
1782 | case ELEMENT_TYPE_R8: // double |
1783 | VolatileStore((UINT64*)pDst, *(UINT64*)&value); |
1784 | break; |
1785 | |
1786 | case ELEMENT_TYPE_I: |
1787 | { |
1788 | INT_PTR valuePtr = (INT_PTR) InvokeUtil::GetIntPtrValue(gc.oValue); |
1789 | VolatileStore((INT_PTR*) pDst, valuePtr); |
1790 | } |
1791 | break; |
1792 | case ELEMENT_TYPE_U: |
1793 | { |
1794 | UINT_PTR valuePtr = (UINT_PTR) InvokeUtil::GetIntPtrValue(gc.oValue); |
1795 | VolatileStore((UINT_PTR*) pDst, valuePtr); |
1796 | } |
1797 | break; |
1798 | |
1799 | case ELEMENT_TYPE_PTR: // pointers |
1800 | if (gc.oValue != 0) { |
1801 | value = 0; |
1802 | if (MscorlibBinder::IsClass(gc.oValue->GetMethodTable(), CLASS__POINTER)) { |
1803 | value = (size_t) InvokeUtil::GetPointerValue(gc.oValue); |
1804 | #ifdef _MSC_VER |
1805 | #pragma warning(disable: 4267) //work-around for compiler |
1806 | #endif |
1807 | VolatileStore((size_t*) pDst, (size_t) value); |
1808 | #ifdef _MSC_VER |
1809 | #pragma warning(default: 4267) |
1810 | #endif |
1811 | break; |
1812 | } |
1813 | } |
1814 | // drop through |
1815 | case ELEMENT_TYPE_FNPTR: |
1816 | { |
1817 | value = 0; |
1818 | if (gc.oValue != 0) { |
1819 | CorElementType objType = gc.oValue->GetTypeHandle().GetInternalCorElementType(); |
1820 | InvokeUtil::CreatePrimitiveValue(objType, objType, gc.oValue, &value); |
1821 | } |
1822 | #ifdef _MSC_VER |
1823 | #pragma warning(disable: 4267) //work-around for compiler |
1824 | #endif |
1825 | VolatileStore((size_t*) pDst, (size_t) value); |
1826 | #ifdef _MSC_VER |
1827 | #pragma warning(default: 4267) |
1828 | #endif |
1829 | } |
1830 | break; |
1831 | |
1832 | case ELEMENT_TYPE_SZARRAY: // Single Dim, Zero |
1833 | case ELEMENT_TYPE_ARRAY: // General Array |
1834 | case ELEMENT_TYPE_CLASS: |
1835 | case ELEMENT_TYPE_OBJECT: |
1836 | SetObjectReferenceUnchecked((OBJECTREF*)pDst, gc.oValue); |
1837 | break; |
1838 | |
1839 | case ELEMENT_TYPE_VALUETYPE: |
1840 | { |
1841 | _ASSERTE(!fieldType.IsTypeDesc()); |
1842 | MethodTable* pMT = fieldType.AsMethodTable(); |
1843 | |
1844 | // If we have a null value then we must create an empty field |
1845 | if (gc.oValue == 0) |
1846 | InitValueClass(pDst, pMT); |
1847 | else { |
1848 | pMT->UnBoxIntoUnchecked(pDst, gc.oValue); |
1849 | } |
1850 | } |
1851 | break; |
1852 | |
1853 | default: |
1854 | _ASSERTE(!"Unknown Type" ); |
1855 | // this is really an impossible condition |
1856 | COMPlusThrow(kNotSupportedException); |
1857 | } |
1858 | |
1859 | lExit: ; |
1860 | HELPER_METHOD_FRAME_END(); |
1861 | } |
1862 | FCIMPLEND |
1863 | |
1864 | void QCALLTYPE ReflectionInvocation::CompileMethod(MethodDesc * pMD) |
1865 | { |
1866 | QCALL_CONTRACT; |
1867 | |
1868 | // Argument is checked on the managed side |
1869 | PRECONDITION(pMD != NULL); |
1870 | |
1871 | if (!pMD->IsPointingToPrestub()) |
1872 | return; |
1873 | |
1874 | BEGIN_QCALL; |
1875 | pMD->DoPrestub(NULL); |
1876 | END_QCALL; |
1877 | } |
1878 | |
1879 | // This method triggers the class constructor for a give type |
1880 | FCIMPL1(void, ReflectionInvocation::RunClassConstructor, ReflectClassBaseObject *pTypeUNSAFE) |
1881 | { |
1882 | FCALL_CONTRACT; |
1883 | |
1884 | REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); |
1885 | |
1886 | if (refType == NULL) |
1887 | FCThrowArgumentVoidEx(kArgumentException, NULL, W("InvalidOperation_HandleIsNotInitialized" )); |
1888 | |
1889 | TypeHandle typeHnd = refType->GetType(); |
1890 | if (typeHnd.IsTypeDesc()) |
1891 | return; |
1892 | |
1893 | MethodTable *pMT = typeHnd.AsMethodTable(); |
1894 | |
1895 | Assembly *pAssem = pMT->GetAssembly(); |
1896 | |
1897 | if (!pMT->IsClassInited()) |
1898 | { |
1899 | HELPER_METHOD_FRAME_BEGIN_1(refType); |
1900 | |
1901 | // We perform the access check only on CoreCLR for backward compatibility. |
1902 | RefSecContext sCtx(InvokeUtil::GetInvocationAccessCheckType()); |
1903 | InvokeUtil::CanAccessClass(&sCtx, pMT); |
1904 | |
1905 | pMT->CheckRestore(); |
1906 | pMT->EnsureInstanceActive(); |
1907 | pMT->CheckRunClassInitThrowing(); |
1908 | |
1909 | HELPER_METHOD_FRAME_END(); |
1910 | } |
1911 | } |
1912 | FCIMPLEND |
1913 | |
1914 | // This method triggers the module constructor for a give module |
1915 | FCIMPL1(void, ReflectionInvocation::RunModuleConstructor, ReflectModuleBaseObject *pModuleUNSAFE) { |
1916 | FCALL_CONTRACT; |
1917 | |
1918 | REFLECTMODULEBASEREF refModule = (REFLECTMODULEBASEREF)ObjectToOBJECTREF(pModuleUNSAFE); |
1919 | |
1920 | if(refModule == NULL) |
1921 | FCThrowArgumentVoidEx(kArgumentException, NULL, W("InvalidOperation_HandleIsNotInitialized" )); |
1922 | |
1923 | Module *pModule = refModule->GetModule(); |
1924 | |
1925 | Assembly *pAssem = pModule->GetAssembly(); |
1926 | |
1927 | DomainFile *pDomainFile = pModule->FindDomainFile(GetAppDomain()); |
1928 | if (pDomainFile==NULL || !pDomainFile->IsActive()) |
1929 | { |
1930 | HELPER_METHOD_FRAME_BEGIN_1(refModule); |
1931 | if(pDomainFile==NULL) |
1932 | pDomainFile=pModule->GetDomainFile(); |
1933 | pDomainFile->EnsureActive(); |
1934 | HELPER_METHOD_FRAME_END(); |
1935 | } |
1936 | } |
1937 | FCIMPLEND |
1938 | |
1939 | static void PrepareMethodHelper(MethodDesc * pMD) |
1940 | { |
1941 | CONTRACTL |
1942 | { |
1943 | THROWS; |
1944 | GC_TRIGGERS; |
1945 | MODE_ANY; |
1946 | } |
1947 | CONTRACTL_END; |
1948 | |
1949 | GCX_PREEMP(); |
1950 | |
1951 | if (pMD->IsPointingToPrestub()) |
1952 | pMD->DoPrestub(NULL); |
1953 | |
1954 | if (pMD->IsWrapperStub()) |
1955 | { |
1956 | pMD = pMD->GetWrappedMethodDesc(); |
1957 | if (pMD->IsPointingToPrestub()) |
1958 | pMD->DoPrestub(NULL); |
1959 | } |
1960 | } |
1961 | |
1962 | // This method triggers a given method to be jitted. CoreCLR implementation of this method triggers jiting of the given method only. |
1963 | // It does not walk a subset of callgraph to provide CER guarantees. |
1964 | FCIMPL3(void, ReflectionInvocation::PrepareMethod, ReflectMethodObject* pMethodUNSAFE, TypeHandle *pInstantiation, UINT32 cInstantiation) |
1965 | { |
1966 | CONTRACTL { |
1967 | FCALL_CHECK; |
1968 | PRECONDITION(CheckPointer(pMethodUNSAFE, NULL_OK)); |
1969 | PRECONDITION(CheckPointer(pInstantiation, NULL_OK)); |
1970 | } |
1971 | CONTRACTL_END; |
1972 | |
1973 | REFLECTMETHODREF refMethod = (REFLECTMETHODREF)ObjectToOBJECTREF(pMethodUNSAFE); |
1974 | |
1975 | HELPER_METHOD_FRAME_BEGIN_1(refMethod); |
1976 | |
1977 | if (refMethod == NULL) |
1978 | COMPlusThrow(kArgumentException, W("InvalidOperation_HandleIsNotInitialized" )); |
1979 | |
1980 | MethodDesc *pMD = refMethod->GetMethod(); |
1981 | |
1982 | if (pMD->IsAbstract()) |
1983 | COMPlusThrow(kArgumentException, W("Argument_CannotPrepareAbstract" )); |
1984 | |
1985 | MethodTable * pExactMT = pMD->GetMethodTable(); |
1986 | if (pInstantiation != NULL) |
1987 | { |
1988 | // We were handed an instantiation, check that the method expects it and the right number of types has been provided (the |
1989 | // caller supplies one array containing the class instantiation immediately followed by the method instantiation). |
1990 | if (cInstantiation != (pMD->GetNumGenericMethodArgs() + pMD->GetNumGenericClassArgs())) |
1991 | COMPlusThrow(kArgumentException, W("Argument_InvalidGenericInstantiation" )); |
1992 | |
1993 | // Check we've got a reasonable looking instantiation. |
1994 | if (!Generics::CheckInstantiation(Instantiation(pInstantiation, cInstantiation))) |
1995 | COMPlusThrow(kArgumentException, W("Argument_InvalidGenericInstantiation" )); |
1996 | for (ULONG i = 0; i < cInstantiation; i++) |
1997 | if (pInstantiation[i].ContainsGenericVariables()) |
1998 | COMPlusThrow(kArgumentException, W("Argument_InvalidGenericInstantiation" )); |
1999 | |
2000 | TypeHandle thExactType = ClassLoader::LoadGenericInstantiationThrowing(pMD->GetModule(), |
2001 | pMD->GetMethodTable()->GetCl(), |
2002 | Instantiation(pInstantiation, pMD->GetNumGenericClassArgs())); |
2003 | pExactMT = thExactType.AsMethodTable(); |
2004 | |
2005 | pMD = MethodDesc::FindOrCreateAssociatedMethodDesc(pMD, |
2006 | pExactMT, |
2007 | FALSE, |
2008 | Instantiation(&pInstantiation[pMD->GetNumGenericClassArgs()], pMD->GetNumGenericMethodArgs()), |
2009 | FALSE); |
2010 | } |
2011 | |
2012 | if (pMD->ContainsGenericVariables()) |
2013 | COMPlusThrow(kArgumentException, W("Argument_InvalidGenericInstantiation" )); |
2014 | |
2015 | PrepareMethodHelper(pMD); |
2016 | |
2017 | HELPER_METHOD_FRAME_END(); |
2018 | } |
2019 | FCIMPLEND |
2020 | |
2021 | // This method triggers target of a given method to be jitted. CoreCLR implementation of this method triggers jiting |
2022 | // of the given method only. It does not walk a subset of callgraph to provide CER guarantees. |
2023 | // In the case of a multi-cast delegate, we rely on the fact that each individual component |
2024 | // was prepared prior to the Combine. |
2025 | FCIMPL1(void, ReflectionInvocation::PrepareDelegate, Object* delegateUNSAFE) |
2026 | { |
2027 | CONTRACTL { |
2028 | FCALL_CHECK; |
2029 | PRECONDITION(CheckPointer(delegateUNSAFE, NULL_OK)); |
2030 | } |
2031 | CONTRACTL_END; |
2032 | |
2033 | if (delegateUNSAFE == NULL) |
2034 | return; |
2035 | |
2036 | OBJECTREF delegate = ObjectToOBJECTREF(delegateUNSAFE); |
2037 | HELPER_METHOD_FRAME_BEGIN_1(delegate); |
2038 | |
2039 | MethodDesc *pMD = COMDelegate::GetMethodDesc(delegate); |
2040 | |
2041 | PrepareMethodHelper(pMD); |
2042 | |
2043 | HELPER_METHOD_FRAME_END(); |
2044 | } |
2045 | FCIMPLEND |
2046 | |
2047 | // This method checks to see if there is sufficient stack to execute the average Framework method. |
2048 | // If there is not, then it throws System.InsufficientExecutionStackException. The limit for each |
2049 | // thread is precomputed when the thread is created. |
2050 | FCIMPL0(void, ReflectionInvocation::EnsureSufficientExecutionStack) |
2051 | { |
2052 | FCALL_CONTRACT; |
2053 | |
2054 | Thread *pThread = GetThread(); |
2055 | |
2056 | // We use the address of a local variable as our "current stack pointer", which is |
2057 | // plenty close enough for the purposes of this method. |
2058 | UINT_PTR current = reinterpret_cast<UINT_PTR>(&pThread); |
2059 | UINT_PTR limit = pThread->GetCachedStackSufficientExecutionLimit(); |
2060 | |
2061 | if (current < limit) |
2062 | { |
2063 | FCThrowVoid(kInsufficientExecutionStackException); |
2064 | } |
2065 | } |
2066 | FCIMPLEND |
2067 | |
2068 | // As with EnsureSufficientExecutionStack, this method checks and returns whether there is |
2069 | // sufficient stack to execute the average Framework method, but rather than throwing, |
2070 | // it simply returns a Boolean: true for sufficient stack space, otherwise false. |
2071 | FCIMPL0(FC_BOOL_RET, ReflectionInvocation::TryEnsureSufficientExecutionStack) |
2072 | { |
2073 | FCALL_CONTRACT; |
2074 | |
2075 | Thread *pThread = GetThread(); |
2076 | |
2077 | // Same logic as EnsureSufficientExecutionStack |
2078 | UINT_PTR current = reinterpret_cast<UINT_PTR>(&pThread); |
2079 | UINT_PTR limit = pThread->GetCachedStackSufficientExecutionLimit(); |
2080 | |
2081 | FC_RETURN_BOOL(current >= limit); |
2082 | } |
2083 | FCIMPLEND |
2084 | |
2085 | struct ECWGCFContext |
2086 | { |
2087 | BOOL fHandled; |
2088 | Frame *pStartFrame; |
2089 | }; |
2090 | |
2091 | // Crawl the stack looking for Thread Abort related information (whether we're executing inside a CER or an error handling clauses |
2092 | // of some sort). |
2093 | StackWalkAction ECWGCFCrawlCallBack(CrawlFrame* pCf, void* data) |
2094 | { |
2095 | CONTRACTL { |
2096 | NOTHROW; |
2097 | GC_NOTRIGGER; |
2098 | } |
2099 | CONTRACTL_END; |
2100 | |
2101 | ECWGCFContext *pData = (ECWGCFContext *)data; |
2102 | |
2103 | Frame *pFrame = pCf->GetFrame(); |
2104 | if (pFrame && pFrame->GetFunction() != NULL && pFrame != pData->pStartFrame) |
2105 | { |
2106 | // We walk through a transition frame, but it is not our start frame. |
2107 | // This means ExecuteCodeWithGuarantee is not at the bottom of stack. |
2108 | pData->fHandled = TRUE; |
2109 | return SWA_ABORT; |
2110 | } |
2111 | |
2112 | MethodDesc *pMD = pCf->GetFunction(); |
2113 | |
2114 | // Non-method frames don't interest us. |
2115 | if (pMD == NULL) |
2116 | return SWA_CONTINUE; |
2117 | |
2118 | if (!pMD->GetModule()->IsSystem()) |
2119 | { |
2120 | // We walk through some user code. This means that ExecuteCodeWithGuarantee is not at the bottom of stack. |
2121 | pData->fHandled = TRUE; |
2122 | return SWA_ABORT; |
2123 | } |
2124 | |
2125 | return SWA_CONTINUE; |
2126 | } |
2127 | |
2128 | struct ECWGC_Param |
2129 | { |
2130 | BOOL fExceptionThrownInTryCode; |
2131 | BOOL fStackOverflow; |
2132 | struct ECWGC_GC *gc; |
2133 | ECWGC_Param() |
2134 | { |
2135 | fExceptionThrownInTryCode = FALSE; |
2136 | fStackOverflow = FALSE; |
2137 | } |
2138 | }; |
2139 | |
2140 | LONG SODetectionFilter(EXCEPTION_POINTERS *ep, void* pv) |
2141 | { |
2142 | WRAPPER_NO_CONTRACT; |
2143 | DefaultCatchFilterParam param(COMPLUS_EXCEPTION_EXECUTE_HANDLER); |
2144 | if (DefaultCatchFilter(ep, ¶m) == EXCEPTION_CONTINUE_EXECUTION) |
2145 | { |
2146 | return EXCEPTION_CONTINUE_EXECUTION; |
2147 | } |
2148 | |
2149 | // Record the fact that an exception occurred while running the try code. |
2150 | ECWGC_Param *pParam= (ECWGC_Param *)pv; |
2151 | pParam->fExceptionThrownInTryCode = TRUE; |
2152 | |
2153 | // We unwind the stack only in the case of a stack overflow. |
2154 | if (ep->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW) |
2155 | { |
2156 | pParam->fStackOverflow = TRUE; |
2157 | return EXCEPTION_EXECUTE_HANDLER; |
2158 | } |
2159 | |
2160 | return EXCEPTION_CONTINUE_SEARCH; |
2161 | } |
2162 | |
2163 | struct ECWGC_GC |
2164 | { |
2165 | DELEGATEREF codeDelegate; |
2166 | DELEGATEREF backoutDelegate; |
2167 | OBJECTREF userData; |
2168 | }; |
2169 | |
2170 | void ExecuteCodeWithGuaranteedCleanupBackout(ECWGC_GC *gc, BOOL fExceptionThrownInTryCode) |
2171 | { |
2172 | // We need to prevent thread aborts from occuring for the duration of the call to the backout code. |
2173 | // Once we enter managed code, the CER will take care of it as well; however without this holder, |
2174 | // MethodDesc::Call would raise a thread abort exception if the thread is currently requesting one. |
2175 | ThreadPreventAbortHolder preventAbort; |
2176 | |
2177 | #ifdef _DEBUG |
2178 | // We have prevented abort on this thread. Normally we don't allow |
2179 | // a thread to enter managed code if abort is prevented. But here the code |
2180 | // requires the thread not be aborted. |
2181 | Thread::DisableAbortCheckHolder dach; |
2182 | #endif |
2183 | |
2184 | GCX_COOP(); |
2185 | |
2186 | PREPARE_NONVIRTUAL_CALLSITE_USING_METHODDESC(g_pExecuteBackoutCodeHelperMethod); |
2187 | |
2188 | DECLARE_ARGHOLDER_ARRAY(args, 3); |
2189 | |
2190 | args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(gc->backoutDelegate); |
2191 | args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(gc->userData); |
2192 | args[ARGNUM_2] = DWORD_TO_ARGHOLDER(fExceptionThrownInTryCode); |
2193 | |
2194 | CRITICAL_CALLSITE; |
2195 | CALL_MANAGED_METHOD_NORET(args); |
2196 | } |
2197 | |
2198 | void ExecuteCodeWithGuaranteedCleanupHelper (ECWGC_GC *gc) |
2199 | { |
2200 | STATIC_CONTRACT_THROWS; |
2201 | STATIC_CONTRACT_MODE_COOPERATIVE; |
2202 | |
2203 | ECWGC_Param param; |
2204 | param.gc = gc; |
2205 | |
2206 | PAL_TRY(ECWGC_Param *, pParamOuter, ¶m) |
2207 | { |
2208 | PAL_TRY(ECWGC_Param *, pParam, pParamOuter) |
2209 | { |
2210 | PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(pParam->gc->codeDelegate->GetMethodPtr()); |
2211 | |
2212 | DECLARE_ARGHOLDER_ARRAY(args, 2); |
2213 | |
2214 | args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(pParam->gc->codeDelegate->GetTarget()); |
2215 | args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(pParam->gc->userData); |
2216 | |
2217 | CALL_MANAGED_METHOD_NORET(args); |
2218 | } |
2219 | PAL_EXCEPT_FILTER(SODetectionFilter) |
2220 | { |
2221 | } |
2222 | PAL_ENDTRY; |
2223 | |
2224 | if (pParamOuter->fStackOverflow) |
2225 | { |
2226 | GCX_COOP_NO_DTOR(); |
2227 | } |
2228 | } |
2229 | PAL_FINALLY |
2230 | { |
2231 | ExecuteCodeWithGuaranteedCleanupBackout(gc, param.fExceptionThrownInTryCode); |
2232 | } |
2233 | PAL_ENDTRY; |
2234 | |
2235 | #ifdef FEATURE_STACK_PROBE |
2236 | if (param.fStackOverflow) |
2237 | COMPlusThrowSO(); |
2238 | #else |
2239 | //This will not be set as clr to managed transition code will terminate the |
2240 | //process if there is an SO before SODetectionFilter() is called. |
2241 | _ASSERTE(!param.fStackOverflow); |
2242 | #endif |
2243 | } |
2244 | |
2245 | // |
2246 | // ExecuteCodeWithGuaranteedCleanup ensures that we will call the backout code delegate even if an SO occurs. We do this by calling the |
2247 | // try delegate from within an EX_TRY/EX_CATCH block that will catch any thrown exceptions and thus cause the stack to be unwound. This |
2248 | // guarantees that the backout delegate is called with at least DEFAULT_ENTRY_PROBE_SIZE pages of stack. After the backout delegate is called, |
2249 | // we re-raise any exceptions that occurred inside the try delegate. Note that any CER that uses large or arbitrary amounts of stack in |
2250 | // it's try block must use ExecuteCodeWithGuaranteedCleanup. |
2251 | // |
2252 | // ExecuteCodeWithGuaranteedCleanup also guarantees that the backount code will be run before any filters higher up on the stack. This |
2253 | // is important to prevent security exploits. |
2254 | // |
2255 | FCIMPL3(void, ReflectionInvocation::ExecuteCodeWithGuaranteedCleanup, Object* codeDelegateUNSAFE, Object* backoutDelegateUNSAFE, Object* userDataUNSAFE) |
2256 | { |
2257 | CONTRACTL { |
2258 | FCALL_CHECK; |
2259 | PRECONDITION(CheckPointer(codeDelegateUNSAFE, NULL_OK)); |
2260 | PRECONDITION(CheckPointer(backoutDelegateUNSAFE, NULL_OK)); |
2261 | PRECONDITION(CheckPointer(userDataUNSAFE, NULL_OK)); |
2262 | } |
2263 | CONTRACTL_END; |
2264 | |
2265 | ECWGC_GC gc; |
2266 | |
2267 | gc.codeDelegate = (DELEGATEREF)ObjectToOBJECTREF(codeDelegateUNSAFE); |
2268 | gc.backoutDelegate = (DELEGATEREF)ObjectToOBJECTREF(backoutDelegateUNSAFE); |
2269 | gc.userData = ObjectToOBJECTREF(userDataUNSAFE); |
2270 | |
2271 | HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); |
2272 | |
2273 | if (gc.codeDelegate == NULL) |
2274 | COMPlusThrowArgumentNull(W("code" )); |
2275 | if (gc.backoutDelegate == NULL) |
2276 | COMPlusThrowArgumentNull(W("backoutCode" )); |
2277 | |
2278 | |
2279 | ExecuteCodeWithGuaranteedCleanupHelper(&gc); |
2280 | |
2281 | HELPER_METHOD_FRAME_END(); |
2282 | } |
2283 | FCIMPLEND |
2284 | |
2285 | |
2286 | FCIMPL4(void, ReflectionInvocation::MakeTypedReference, TypedByRef * value, Object* targetUNSAFE, ArrayBase* fldsUNSAFE, ReflectClassBaseObject *pFieldTypeUNSAFE) |
2287 | { |
2288 | CONTRACTL { |
2289 | FCALL_CHECK; |
2290 | PRECONDITION(CheckPointer(targetUNSAFE)); |
2291 | PRECONDITION(CheckPointer(fldsUNSAFE)); |
2292 | } |
2293 | CONTRACTL_END; |
2294 | |
2295 | DWORD offset = 0; |
2296 | |
2297 | struct _gc |
2298 | { |
2299 | OBJECTREF target; |
2300 | BASEARRAYREF flds; |
2301 | REFLECTCLASSBASEREF refFieldType; |
2302 | } gc; |
2303 | gc.target = (OBJECTREF) targetUNSAFE; |
2304 | gc.flds = (BASEARRAYREF) fldsUNSAFE; |
2305 | gc.refFieldType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pFieldTypeUNSAFE); |
2306 | |
2307 | TypeHandle fieldType = gc.refFieldType->GetType(); |
2308 | |
2309 | HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); |
2310 | GCPROTECT_BEGININTERIOR (value) |
2311 | |
2312 | DWORD cnt = gc.flds->GetNumComponents(); |
2313 | FieldDesc** fields = (FieldDesc**)gc.flds->GetDataPtr(); |
2314 | for (DWORD i = 0; i < cnt; i++) { |
2315 | FieldDesc* pField = fields[i]; |
2316 | offset += pField->GetOffset(); |
2317 | } |
2318 | |
2319 | // Fields already are prohibted from having ArgIterator and RuntimeArgumentHandles |
2320 | _ASSERTE(!gc.target->GetTypeHandle().GetMethodTable()->IsByRefLike()); |
2321 | |
2322 | // Create the ByRef |
2323 | value->data = ((BYTE *)(gc.target->GetAddress() + offset)) + sizeof(Object); |
2324 | value->type = fieldType; |
2325 | |
2326 | GCPROTECT_END(); |
2327 | HELPER_METHOD_FRAME_END(); |
2328 | } |
2329 | FCIMPLEND |
2330 | |
2331 | FCIMPL2(void, ReflectionInvocation::SetTypedReference, TypedByRef * target, Object* objUNSAFE) { |
2332 | FCALL_CONTRACT; |
2333 | |
2334 | // <TODO>@TODO: We fixed serious bugs in this method very late in the endgame |
2335 | // for V1 RTM. So it was decided to disable this API (nobody would seem to |
2336 | // be using it anyway). If this API is enabled again, the implementation should |
2337 | // be similar to COMArrayInfo::SetValue. |
2338 | // </TODO> |
2339 | HELPER_METHOD_FRAME_BEGIN_0(); |
2340 | COMPlusThrow(kNotSupportedException); |
2341 | HELPER_METHOD_FRAME_END(); |
2342 | } |
2343 | FCIMPLEND |
2344 | |
2345 | |
2346 | // This is an internal helper function to TypedReference class. |
2347 | // It extracts the object from the typed reference. |
2348 | FCIMPL1(Object*, ReflectionInvocation::TypedReferenceToObject, TypedByRef * value) { |
2349 | FCALL_CONTRACT; |
2350 | |
2351 | OBJECTREF Obj = NULL; |
2352 | |
2353 | TypeHandle th(value->type); |
2354 | |
2355 | if (th.IsNull()) |
2356 | FCThrowRes(kArgumentNullException, W("ArgumentNull_TypedRefType" )); |
2357 | |
2358 | MethodTable* pMT = th.GetMethodTable(); |
2359 | PREFIX_ASSUME(NULL != pMT); |
2360 | |
2361 | if (pMT->IsValueType()) |
2362 | { |
2363 | // value->data is protected by the caller |
2364 | HELPER_METHOD_FRAME_BEGIN_RET_1(Obj); |
2365 | |
2366 | Obj = pMT->Box(value->data); |
2367 | |
2368 | HELPER_METHOD_FRAME_END(); |
2369 | } |
2370 | else { |
2371 | Obj = ObjectToOBJECTREF(*((Object**)value->data)); |
2372 | } |
2373 | |
2374 | return OBJECTREFToObject(Obj); |
2375 | } |
2376 | FCIMPLEND |
2377 | |
2378 | FCIMPL2_IV(Object*, ReflectionInvocation::CreateEnum, ReflectClassBaseObject *pTypeUNSAFE, INT64 value) { |
2379 | FCALL_CONTRACT; |
2380 | |
2381 | REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); |
2382 | |
2383 | TypeHandle typeHandle = refType->GetType(); |
2384 | _ASSERTE(typeHandle.IsEnum()); |
2385 | OBJECTREF obj = NULL; |
2386 | HELPER_METHOD_FRAME_BEGIN_RET_1(refType); |
2387 | MethodTable *pEnumMT = typeHandle.AsMethodTable(); |
2388 | obj = pEnumMT->Box(ArgSlotEndianessFixup ((ARG_SLOT*)&value, |
2389 | pEnumMT->GetNumInstanceFieldBytes())); |
2390 | |
2391 | HELPER_METHOD_FRAME_END(); |
2392 | return OBJECTREFToObject(obj); |
2393 | } |
2394 | FCIMPLEND |
2395 | |
2396 | #ifdef FEATURE_COMINTEROP |
2397 | |
2398 | static void TryGetClassFromProgID(STRINGREF className, STRINGREF server, OBJECTREF* pRefClass, DWORD bThrowOnError) { |
2399 | CONTRACTL { |
2400 | THROWS; |
2401 | GC_TRIGGERS; |
2402 | MODE_COOPERATIVE; |
2403 | } |
2404 | CONTRACTL_END; |
2405 | |
2406 | EX_TRY |
2407 | { |
2408 | // NOTE: this call enables GC |
2409 | GetComClassFromProgID(className, server, pRefClass); |
2410 | } |
2411 | EX_CATCH |
2412 | { |
2413 | if (bThrowOnError) |
2414 | { |
2415 | EX_RETHROW; |
2416 | } |
2417 | } |
2418 | EX_END_CATCH(SwallowAllExceptions) |
2419 | } |
2420 | |
2421 | // GetClassFromProgID |
2422 | // This method will return a Class object for a COM Classic object based |
2423 | // upon its ProgID. The COM Classic object is found and a wrapper object created |
2424 | FCIMPL3(Object*, ReflectionInvocation::GetClassFromProgID, StringObject* classNameUNSAFE, |
2425 | StringObject* serverUNSAFE, |
2426 | CLR_BOOL bThrowOnError) { |
2427 | FCALL_CONTRACT; |
2428 | |
2429 | REFLECTCLASSBASEREF refClass = NULL; |
2430 | STRINGREF className = (STRINGREF) classNameUNSAFE; |
2431 | STRINGREF server = (STRINGREF) serverUNSAFE; |
2432 | |
2433 | HELPER_METHOD_FRAME_BEGIN_RET_2(className, server); |
2434 | |
2435 | GCPROTECT_BEGIN(refClass) |
2436 | |
2437 | // Since we will be returning a type that represents a COM component, we need |
2438 | // to make sure COM is started before we return it. |
2439 | EnsureComStarted(); |
2440 | |
2441 | // Make sure a prog id was provided |
2442 | if (className == NULL) |
2443 | COMPlusThrowArgumentNull(W("progID" ),W("ArgumentNull_String" )); |
2444 | |
2445 | TryGetClassFromProgID(className, server, (OBJECTREF*) &refClass, bThrowOnError); |
2446 | GCPROTECT_END(); |
2447 | |
2448 | HELPER_METHOD_FRAME_END(); |
2449 | return OBJECTREFToObject(refClass); |
2450 | } |
2451 | FCIMPLEND |
2452 | |
2453 | static void TryGetClassFromCLSID(GUID clsid, STRINGREF server, OBJECTREF* pRefClass, DWORD bThrowOnError) { |
2454 | CONTRACTL { |
2455 | THROWS; |
2456 | GC_TRIGGERS; |
2457 | MODE_COOPERATIVE; |
2458 | } |
2459 | CONTRACTL_END; |
2460 | |
2461 | EX_TRY |
2462 | { |
2463 | // NOTE: this call enables GC |
2464 | GetComClassFromCLSID(clsid, server, pRefClass); |
2465 | } |
2466 | EX_CATCH |
2467 | { |
2468 | if (bThrowOnError) |
2469 | { |
2470 | EX_RETHROW; |
2471 | } |
2472 | } |
2473 | EX_END_CATCH(SwallowAllExceptions) |
2474 | } |
2475 | |
2476 | // GetClassFromCLSID |
2477 | // This method will return a Class object for a COM Classic object based |
2478 | // upon its ProgID. The COM Classic object is found and a wrapper object created |
2479 | FCIMPL3(Object*, ReflectionInvocation::GetClassFromCLSID, GUID clsid, StringObject* serverUNSAFE, CLR_BOOL bThrowOnError) { |
2480 | FCALL_CONTRACT; |
2481 | |
2482 | struct _gc { |
2483 | REFLECTCLASSBASEREF refClass; |
2484 | STRINGREF server; |
2485 | } gc; |
2486 | |
2487 | gc.refClass = NULL; |
2488 | gc.server = (STRINGREF) serverUNSAFE; |
2489 | |
2490 | HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc.server); |
2491 | |
2492 | // Since we will be returning a type that represents a COM component, we need |
2493 | // to make sure COM is started before we return it. |
2494 | EnsureComStarted(); |
2495 | |
2496 | TryGetClassFromCLSID(clsid, gc.server, (OBJECTREF*) &gc.refClass, bThrowOnError); |
2497 | |
2498 | HELPER_METHOD_FRAME_END(); |
2499 | return OBJECTREFToObject(gc.refClass); |
2500 | } |
2501 | FCIMPLEND |
2502 | |
2503 | |
2504 | FCIMPL8(Object*, ReflectionInvocation::InvokeDispMethod, ReflectClassBaseObject* refThisUNSAFE, |
2505 | StringObject* nameUNSAFE, |
2506 | INT32 invokeAttr, |
2507 | Object* targetUNSAFE, |
2508 | PTRArray* argsUNSAFE, |
2509 | PTRArray* byrefModifiersUNSAFE, |
2510 | LCID lcid, |
2511 | PTRArray* namedParametersUNSAFE) { |
2512 | FCALL_CONTRACT; |
2513 | |
2514 | struct _gc |
2515 | { |
2516 | REFLECTCLASSBASEREF refThis; |
2517 | STRINGREF name; |
2518 | OBJECTREF target; |
2519 | PTRARRAYREF args; |
2520 | PTRARRAYREF byrefModifiers; |
2521 | PTRARRAYREF namedParameters; |
2522 | OBJECTREF RetObj; |
2523 | } gc; |
2524 | |
2525 | gc.refThis = (REFLECTCLASSBASEREF) refThisUNSAFE; |
2526 | gc.name = (STRINGREF) nameUNSAFE; |
2527 | gc.target = (OBJECTREF) targetUNSAFE; |
2528 | gc.args = (PTRARRAYREF) argsUNSAFE; |
2529 | gc.byrefModifiers = (PTRARRAYREF) byrefModifiersUNSAFE; |
2530 | gc.namedParameters = (PTRARRAYREF) namedParametersUNSAFE; |
2531 | gc.RetObj = NULL; |
2532 | |
2533 | HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); |
2534 | |
2535 | _ASSERTE(gc.target != NULL); |
2536 | _ASSERTE(gc.target->GetMethodTable()->IsComObjectType()); |
2537 | |
2538 | WORD flags = 0; |
2539 | if (invokeAttr & BINDER_InvokeMethod) |
2540 | flags |= DISPATCH_METHOD; |
2541 | if (invokeAttr & BINDER_GetProperty) |
2542 | flags |= DISPATCH_PROPERTYGET; |
2543 | if (invokeAttr & BINDER_SetProperty) |
2544 | flags = DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF; |
2545 | if (invokeAttr & BINDER_PutDispProperty) |
2546 | flags = DISPATCH_PROPERTYPUT; |
2547 | if (invokeAttr & BINDER_PutRefDispProperty) |
2548 | flags = DISPATCH_PROPERTYPUTREF; |
2549 | if (invokeAttr & BINDER_CreateInstance) |
2550 | flags = DISPATCH_CONSTRUCT; |
2551 | |
2552 | IUInvokeDispMethod(&gc.refThis, |
2553 | &gc.target, |
2554 | (OBJECTREF*)&gc.name, |
2555 | NULL, |
2556 | (OBJECTREF*)&gc.args, |
2557 | (OBJECTREF*)&gc.byrefModifiers, |
2558 | (OBJECTREF*)&gc.namedParameters, |
2559 | &gc.RetObj, |
2560 | lcid, |
2561 | flags, |
2562 | invokeAttr & BINDER_IgnoreReturn, |
2563 | invokeAttr & BINDER_IgnoreCase); |
2564 | |
2565 | HELPER_METHOD_FRAME_END(); |
2566 | return OBJECTREFToObject(gc.RetObj); |
2567 | } |
2568 | FCIMPLEND |
2569 | #endif // FEATURE_COMINTEROP |
2570 | |
2571 | FCIMPL2(void, ReflectionInvocation::GetGUID, ReflectClassBaseObject* refThisUNSAFE, GUID * result) { |
2572 | FCALL_CONTRACT; |
2573 | |
2574 | REFLECTCLASSBASEREF refThis = (REFLECTCLASSBASEREF) refThisUNSAFE; |
2575 | |
2576 | HELPER_METHOD_FRAME_BEGIN_1(refThis); |
2577 | GCPROTECT_BEGININTERIOR (result); |
2578 | |
2579 | if (result == NULL || refThis == NULL) |
2580 | COMPlusThrow(kNullReferenceException); |
2581 | |
2582 | TypeHandle type = refThis->GetType(); |
2583 | if (type.IsTypeDesc()) { |
2584 | memset(result,0,sizeof(GUID)); |
2585 | goto lExit; |
2586 | } |
2587 | |
2588 | #ifdef FEATURE_COMINTEROP |
2589 | if (IsComObjectClass(type)) |
2590 | { |
2591 | SyncBlock* pSyncBlock = refThis->GetSyncBlock(); |
2592 | |
2593 | #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION |
2594 | ComClassFactory* pComClsFac = pSyncBlock->GetInteropInfo()->GetComClassFactory(); |
2595 | if (pComClsFac) |
2596 | { |
2597 | memcpyNoGCRefs(result, &pComClsFac->m_rclsid, sizeof(GUID)); |
2598 | } |
2599 | else |
2600 | #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION |
2601 | { |
2602 | memset(result, 0, sizeof(GUID)); |
2603 | } |
2604 | |
2605 | goto lExit; |
2606 | } |
2607 | #endif // FEATURE_COMINTEROP |
2608 | |
2609 | GUID guid; |
2610 | type.AsMethodTable()->GetGuid(&guid, TRUE); |
2611 | memcpyNoGCRefs(result, &guid, sizeof(GUID)); |
2612 | |
2613 | lExit: ; |
2614 | GCPROTECT_END(); |
2615 | HELPER_METHOD_FRAME_END(); |
2616 | } |
2617 | FCIMPLEND |
2618 | |
2619 | //************************************************************************************************* |
2620 | //************************************************************************************************* |
2621 | //************************************************************************************************* |
2622 | // ReflectionSerialization |
2623 | //************************************************************************************************* |
2624 | //************************************************************************************************* |
2625 | //************************************************************************************************* |
2626 | FCIMPL1(Object*, ReflectionSerialization::GetUninitializedObject, ReflectClassBaseObject* objTypeUNSAFE) { |
2627 | FCALL_CONTRACT; |
2628 | |
2629 | OBJECTREF retVal = NULL; |
2630 | REFLECTCLASSBASEREF objType = (REFLECTCLASSBASEREF) objTypeUNSAFE; |
2631 | |
2632 | HELPER_METHOD_FRAME_BEGIN_RET_NOPOLL(); |
2633 | |
2634 | if (objType == NULL) { |
2635 | COMPlusThrowArgumentNull(W("type" ), W("ArgumentNull_Type" )); |
2636 | } |
2637 | |
2638 | TypeHandle type = objType->GetType(); |
2639 | |
2640 | // Don't allow arrays, pointers, byrefs or function pointers. |
2641 | if (type.IsTypeDesc()) |
2642 | COMPlusThrow(kArgumentException, W("Argument_InvalidValue" )); |
2643 | |
2644 | MethodTable *pMT = type.GetMethodTable(); |
2645 | PREFIX_ASSUME(pMT != NULL); |
2646 | |
2647 | //We don't allow unitialized strings. |
2648 | if (pMT == g_pStringClass) { |
2649 | COMPlusThrow(kArgumentException, W("Argument_NoUninitializedStrings" )); |
2650 | } |
2651 | |
2652 | // if this is an abstract class or an interface type then we will |
2653 | // fail this |
2654 | if (pMT->IsAbstract()) { |
2655 | COMPlusThrow(kMemberAccessException,W("Acc_CreateAbst" )); |
2656 | } |
2657 | |
2658 | if (pMT->ContainsGenericVariables()) { |
2659 | COMPlusThrow(kMemberAccessException,W("Acc_CreateGeneric" )); |
2660 | } |
2661 | |
2662 | if (pMT->IsByRefLike()) { |
2663 | COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike" )); |
2664 | } |
2665 | |
2666 | // Never allow allocation of generics actually instantiated over __Canon |
2667 | if (pMT->IsSharedByGenericInstantiations()) { |
2668 | COMPlusThrow(kNotSupportedException, W("NotSupported_Type" )); |
2669 | } |
2670 | |
2671 | // Never allow the allocation of an unitialized ContextBoundObject derived type, these must always be created with a paired |
2672 | // transparent proxy or the jit will get confused. |
2673 | |
2674 | #ifdef FEATURE_COMINTEROP |
2675 | // Also do not allow allocation of uninitialized RCWs (COM objects). |
2676 | if (pMT->IsComObjectType()) |
2677 | COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation" )); |
2678 | #endif // FEATURE_COMINTEROP |
2679 | |
2680 | // If it is a nullable, return the underlying type instead. |
2681 | if (Nullable::IsNullableType(pMT)) |
2682 | pMT = pMT->GetInstantiation()[0].GetMethodTable(); |
2683 | |
2684 | retVal = pMT->Allocate(); |
2685 | |
2686 | HELPER_METHOD_FRAME_END(); |
2687 | return OBJECTREFToObject(retVal); |
2688 | } |
2689 | FCIMPLEND |
2690 | |
2691 | //************************************************************************************************* |
2692 | //************************************************************************************************* |
2693 | //************************************************************************************************* |
2694 | // ReflectionEnum |
2695 | //************************************************************************************************* |
2696 | //************************************************************************************************* |
2697 | //************************************************************************************************* |
2698 | |
2699 | FCIMPL1(Object *, ReflectionEnum::InternalGetEnumUnderlyingType, ReflectClassBaseObject *target) { |
2700 | FCALL_CONTRACT; |
2701 | |
2702 | VALIDATEOBJECT(target); |
2703 | TypeHandle th = target->GetType(); |
2704 | if (!th.IsEnum()) |
2705 | FCThrowArgument(NULL, NULL); |
2706 | |
2707 | OBJECTREF result = NULL; |
2708 | |
2709 | HELPER_METHOD_FRAME_BEGIN_RET_0(); |
2710 | MethodTable *pMT = MscorlibBinder::GetElementType(th.AsMethodTable()->GetInternalCorElementType()); |
2711 | result = pMT->GetManagedClassObject(); |
2712 | HELPER_METHOD_FRAME_END(); |
2713 | |
2714 | return OBJECTREFToObject(result); |
2715 | } |
2716 | FCIMPLEND |
2717 | |
2718 | FCIMPL1(INT32, ReflectionEnum::InternalGetCorElementType, Object *pRefThis) { |
2719 | FCALL_CONTRACT; |
2720 | |
2721 | VALIDATEOBJECT(pRefThis); |
2722 | if (pRefThis == NULL) |
2723 | FCThrowArgumentNull(NULL); |
2724 | |
2725 | return pRefThis->GetMethodTable()->GetInternalCorElementType(); |
2726 | } |
2727 | FCIMPLEND |
2728 | |
2729 | //******************************************************************************* |
2730 | struct TempEnumValue |
2731 | { |
2732 | LPCUTF8 name; |
2733 | UINT64 value; |
2734 | }; |
2735 | |
2736 | //******************************************************************************* |
2737 | class TempEnumValueSorter : public CQuickSort<TempEnumValue> |
2738 | { |
2739 | public: |
2740 | TempEnumValueSorter(TempEnumValue *pArray, SSIZE_T iCount) |
2741 | : CQuickSort<TempEnumValue>(pArray, iCount) { LIMITED_METHOD_CONTRACT; } |
2742 | |
2743 | int Compare(TempEnumValue *pFirst, TempEnumValue *pSecond) |
2744 | { |
2745 | LIMITED_METHOD_CONTRACT; |
2746 | |
2747 | if (pFirst->value == pSecond->value) |
2748 | return 0; |
2749 | if (pFirst->value > pSecond->value) |
2750 | return 1; |
2751 | else |
2752 | return -1; |
2753 | } |
2754 | }; |
2755 | |
2756 | void QCALLTYPE ReflectionEnum::GetEnumValuesAndNames(EnregisteredTypeHandle pEnumType, QCall::ObjectHandleOnStack pReturnValues, QCall::ObjectHandleOnStack pReturnNames, BOOL fGetNames) |
2757 | { |
2758 | QCALL_CONTRACT; |
2759 | |
2760 | BEGIN_QCALL; |
2761 | |
2762 | TypeHandle th = TypeHandle::FromPtr(pEnumType); |
2763 | |
2764 | if (!th.IsEnum()) |
2765 | COMPlusThrow(kArgumentException, W("Arg_MustBeEnum" )); |
2766 | |
2767 | MethodTable *pMT = th.AsMethodTable(); |
2768 | |
2769 | IMDInternalImport *pImport = pMT->GetMDImport(); |
2770 | |
2771 | StackSArray<TempEnumValue> temps; |
2772 | UINT64 previousValue = 0; |
2773 | |
2774 | HENUMInternalHolder fieldEnum(pImport); |
2775 | fieldEnum.EnumInit(mdtFieldDef, pMT->GetCl()); |
2776 | |
2777 | // |
2778 | // Note that we're fine treating signed types as unsigned, because all we really |
2779 | // want to do is sort them based on a convenient strong ordering. |
2780 | // |
2781 | |
2782 | BOOL sorted = TRUE; |
2783 | |
2784 | CorElementType type = pMT->GetInternalCorElementType(); |
2785 | |
2786 | mdFieldDef field; |
2787 | while (pImport->EnumNext(&fieldEnum, &field)) |
2788 | { |
2789 | DWORD dwFlags; |
2790 | IfFailThrow(pImport->GetFieldDefProps(field, &dwFlags)); |
2791 | if (IsFdStatic(dwFlags)) |
2792 | { |
2793 | TempEnumValue temp; |
2794 | |
2795 | if (fGetNames) |
2796 | IfFailThrow(pImport->GetNameOfFieldDef(field, &temp.name)); |
2797 | |
2798 | UINT64 value = 0; |
2799 | |
2800 | MDDefaultValue defaultValue; |
2801 | IfFailThrow(pImport->GetDefaultValue(field, &defaultValue)); |
2802 | |
2803 | // The following code assumes that the address of all union members is the same. |
2804 | static_assert_no_msg(offsetof(MDDefaultValue, m_byteValue) == offsetof(MDDefaultValue, m_usValue)); |
2805 | static_assert_no_msg(offsetof(MDDefaultValue, m_ulValue) == offsetof(MDDefaultValue, m_ullValue)); |
2806 | PVOID pValue = &defaultValue.m_byteValue; |
2807 | |
2808 | switch (type) { |
2809 | case ELEMENT_TYPE_I1: |
2810 | value = *((INT8 *)pValue); |
2811 | break; |
2812 | |
2813 | case ELEMENT_TYPE_U1: |
2814 | case ELEMENT_TYPE_BOOLEAN: |
2815 | value = *((UINT8 *)pValue); |
2816 | break; |
2817 | |
2818 | case ELEMENT_TYPE_I2: |
2819 | value = *((INT16 *)pValue); |
2820 | break; |
2821 | |
2822 | case ELEMENT_TYPE_U2: |
2823 | case ELEMENT_TYPE_CHAR: |
2824 | value = *((UINT16 *)pValue); |
2825 | break; |
2826 | |
2827 | case ELEMENT_TYPE_I4: |
2828 | IN_WIN32(case ELEMENT_TYPE_I:) |
2829 | value = *((INT32 *)pValue); |
2830 | break; |
2831 | |
2832 | case ELEMENT_TYPE_U4: |
2833 | IN_WIN32(case ELEMENT_TYPE_U:) |
2834 | value = *((UINT32 *)pValue); |
2835 | break; |
2836 | |
2837 | case ELEMENT_TYPE_I8: |
2838 | case ELEMENT_TYPE_U8: |
2839 | IN_WIN64(case ELEMENT_TYPE_I:) |
2840 | IN_WIN64(case ELEMENT_TYPE_U:) |
2841 | value = *((INT64 *)pValue); |
2842 | break; |
2843 | |
2844 | default: |
2845 | break; |
2846 | } |
2847 | |
2848 | temp.value = value; |
2849 | |
2850 | // |
2851 | // Check to see if we are already sorted. This may seem extraneous, but is |
2852 | // actually probably the normal case. |
2853 | // |
2854 | |
2855 | if (previousValue > value) |
2856 | sorted = FALSE; |
2857 | previousValue = value; |
2858 | |
2859 | temps.Append(temp); |
2860 | } |
2861 | } |
2862 | |
2863 | TempEnumValue * pTemps = &(temps[0]); |
2864 | DWORD cFields = temps.GetCount(); |
2865 | |
2866 | if (!sorted) |
2867 | { |
2868 | TempEnumValueSorter sorter(pTemps, cFields); |
2869 | sorter.Sort(); |
2870 | } |
2871 | |
2872 | { |
2873 | GCX_COOP(); |
2874 | |
2875 | struct gc { |
2876 | I8ARRAYREF values; |
2877 | PTRARRAYREF names; |
2878 | } gc; |
2879 | gc.values = NULL; |
2880 | gc.names = NULL; |
2881 | |
2882 | GCPROTECT_BEGIN(gc); |
2883 | |
2884 | { |
2885 | gc.values = (I8ARRAYREF) AllocatePrimitiveArray(ELEMENT_TYPE_U8, cFields); |
2886 | |
2887 | INT64 *pToValues = gc.values->GetDirectPointerToNonObjectElements(); |
2888 | |
2889 | for (DWORD i = 0; i < cFields; i++) { |
2890 | pToValues[i] = pTemps[i].value; |
2891 | } |
2892 | |
2893 | pReturnValues.Set(gc.values); |
2894 | } |
2895 | |
2896 | if (fGetNames) |
2897 | { |
2898 | gc.names = (PTRARRAYREF) AllocateObjectArray(cFields, g_pStringClass); |
2899 | |
2900 | for (DWORD i = 0; i < cFields; i++) { |
2901 | STRINGREF str = StringObject::NewString(pTemps[i].name); |
2902 | gc.names->SetAt(i, str); |
2903 | } |
2904 | |
2905 | pReturnNames.Set(gc.names); |
2906 | } |
2907 | |
2908 | GCPROTECT_END(); |
2909 | } |
2910 | |
2911 | END_QCALL; |
2912 | } |
2913 | |
2914 | FCIMPL2_IV(Object*, ReflectionEnum::InternalBoxEnum, ReflectClassBaseObject* target, INT64 value) { |
2915 | FCALL_CONTRACT; |
2916 | |
2917 | VALIDATEOBJECT(target); |
2918 | OBJECTREF ret = NULL; |
2919 | |
2920 | MethodTable* pMT = target->GetType().AsMethodTable(); |
2921 | HELPER_METHOD_FRAME_BEGIN_RET_0(); |
2922 | |
2923 | ret = pMT->Box(ArgSlotEndianessFixup((ARG_SLOT*)&value, pMT->GetNumInstanceFieldBytes())); |
2924 | |
2925 | HELPER_METHOD_FRAME_END(); |
2926 | return OBJECTREFToObject(ret); |
2927 | } |
2928 | FCIMPLEND |
2929 | |
2930 | //************************************************************************************************* |
2931 | //************************************************************************************************* |
2932 | //************************************************************************************************* |
2933 | // ReflectionBinder |
2934 | //************************************************************************************************* |
2935 | //************************************************************************************************* |
2936 | //************************************************************************************************* |
2937 | |
2938 | FCIMPL2(FC_BOOL_RET, ReflectionBinder::DBCanConvertPrimitive, ReflectClassBaseObject* source, ReflectClassBaseObject* target) { |
2939 | FCALL_CONTRACT; |
2940 | |
2941 | VALIDATEOBJECT(source); |
2942 | VALIDATEOBJECT(target); |
2943 | |
2944 | CorElementType tSRC = source->GetType().GetSignatureCorElementType(); |
2945 | CorElementType tTRG = target->GetType().GetSignatureCorElementType(); |
2946 | |
2947 | FC_RETURN_BOOL(InvokeUtil::IsPrimitiveType(tTRG) && InvokeUtil::CanPrimitiveWiden(tTRG, tSRC)); |
2948 | } |
2949 | FCIMPLEND |
2950 | |
2951 | FCIMPL2(FC_BOOL_RET, ReflectionBinder::DBCanConvertObjectPrimitive, Object* sourceObj, ReflectClassBaseObject* target) { |
2952 | FCALL_CONTRACT; |
2953 | |
2954 | VALIDATEOBJECT(sourceObj); |
2955 | VALIDATEOBJECT(target); |
2956 | |
2957 | if (sourceObj == 0) |
2958 | FC_RETURN_BOOL(true); |
2959 | |
2960 | TypeHandle th(sourceObj->GetMethodTable()); |
2961 | CorElementType tSRC = th.GetVerifierCorElementType(); |
2962 | |
2963 | CorElementType tTRG = target->GetType().GetSignatureCorElementType(); |
2964 | FC_RETURN_BOOL(InvokeUtil::IsPrimitiveType(tTRG) && InvokeUtil::CanPrimitiveWiden(tTRG, tSRC)); |
2965 | } |
2966 | FCIMPLEND |
2967 | |
2968 | FCIMPL2(FC_BOOL_RET, ReflectionEnum::InternalEquals, Object *pRefThis, Object* pRefTarget) |
2969 | { |
2970 | FCALL_CONTRACT; |
2971 | |
2972 | VALIDATEOBJECT(pRefThis); |
2973 | BOOL ret = false; |
2974 | if (pRefTarget == NULL) { |
2975 | FC_RETURN_BOOL(ret); |
2976 | } |
2977 | |
2978 | if( pRefThis == pRefTarget) |
2979 | FC_RETURN_BOOL(true); |
2980 | |
2981 | //Make sure we are comparing same type. |
2982 | MethodTable* pMTThis = pRefThis->GetMethodTable(); |
2983 | _ASSERTE(!pMTThis->IsArray()); // bunch of assumptions about arrays wrong. |
2984 | if ( pMTThis != pRefTarget->GetMethodTable()) { |
2985 | FC_RETURN_BOOL(ret); |
2986 | } |
2987 | |
2988 | void * pThis = pRefThis->UnBox(); |
2989 | void * pTarget = pRefTarget->UnBox(); |
2990 | switch (pMTThis->GetNumInstanceFieldBytes()) { |
2991 | case 1: |
2992 | ret = (*(UINT8*)pThis == *(UINT8*)pTarget); |
2993 | break; |
2994 | case 2: |
2995 | ret = (*(UINT16*)pThis == *(UINT16*)pTarget); |
2996 | break; |
2997 | case 4: |
2998 | ret = (*(UINT32*)pThis == *(UINT32*)pTarget); |
2999 | break; |
3000 | case 8: |
3001 | ret = (*(UINT64*)pThis == *(UINT64*)pTarget); |
3002 | break; |
3003 | default: |
3004 | // should not reach here. |
3005 | UNREACHABLE_MSG("Incorrect Enum Type size!" ); |
3006 | break; |
3007 | } |
3008 | |
3009 | FC_RETURN_BOOL(ret); |
3010 | } |
3011 | FCIMPLEND |
3012 | |
3013 | // preform (this & flags) != flags |
3014 | FCIMPL2(FC_BOOL_RET, ReflectionEnum::InternalHasFlag, Object *pRefThis, Object* pRefFlags) |
3015 | { |
3016 | FCALL_CONTRACT; |
3017 | |
3018 | VALIDATEOBJECT(pRefThis); |
3019 | |
3020 | BOOL cmp = false; |
3021 | |
3022 | _ASSERTE(pRefFlags != NULL); // Enum.cs would have thrown ArgumentNullException before calling into InternalHasFlag |
3023 | |
3024 | VALIDATEOBJECT(pRefFlags); |
3025 | |
3026 | void * pThis = pRefThis->UnBox(); |
3027 | void * pFlags = pRefFlags->UnBox(); |
3028 | |
3029 | MethodTable* pMTThis = pRefThis->GetMethodTable(); |
3030 | |
3031 | _ASSERTE(!pMTThis->IsArray()); // bunch of assumptions about arrays wrong. |
3032 | _ASSERTE(pMTThis->GetNumInstanceFieldBytes() == pRefFlags->GetMethodTable()->GetNumInstanceFieldBytes()); // Enum.cs verifies that the types are Equivalent |
3033 | |
3034 | switch (pMTThis->GetNumInstanceFieldBytes()) { |
3035 | case 1: |
3036 | cmp = ((*(UINT8*)pThis & *(UINT8*)pFlags) == *(UINT8*)pFlags); |
3037 | break; |
3038 | case 2: |
3039 | cmp = ((*(UINT16*)pThis & *(UINT16*)pFlags) == *(UINT16*)pFlags); |
3040 | break; |
3041 | case 4: |
3042 | cmp = ((*(UINT32*)pThis & *(UINT32*)pFlags) == *(UINT32*)pFlags); |
3043 | break; |
3044 | case 8: |
3045 | cmp = ((*(UINT64*)pThis & *(UINT64*)pFlags) == *(UINT64*)pFlags); |
3046 | break; |
3047 | default: |
3048 | // should not reach here. |
3049 | UNREACHABLE_MSG("Incorrect Enum Type size!" ); |
3050 | break; |
3051 | } |
3052 | |
3053 | FC_RETURN_BOOL(cmp); |
3054 | } |
3055 | FCIMPLEND |
3056 | |
3057 | // compare two boxed enums using their underlying enum type |
3058 | FCIMPL2(int, ReflectionEnum::InternalCompareTo, Object *pRefThis, Object* pRefTarget) |
3059 | { |
3060 | FCALL_CONTRACT; |
3061 | |
3062 | const int retIncompatibleMethodTables = 2; // indicates that the method tables did not match |
3063 | const int retInvalidEnumType = 3; // indicates that the enum was of an unknown/unsupported unerlying type |
3064 | |
3065 | VALIDATEOBJECT(pRefThis); |
3066 | |
3067 | if (pRefTarget == NULL) { |
3068 | return 1; // all values are greater than null |
3069 | } |
3070 | |
3071 | if( pRefThis == pRefTarget) |
3072 | return 0; |
3073 | |
3074 | VALIDATEOBJECT(pRefTarget); |
3075 | |
3076 | //Make sure we are comparing same type. |
3077 | MethodTable* pMTThis = pRefThis->GetMethodTable(); |
3078 | |
3079 | _ASSERTE(pMTThis->IsEnum()); |
3080 | |
3081 | if ( pMTThis != pRefTarget->GetMethodTable()) { |
3082 | return retIncompatibleMethodTables; // error case, types incompatible |
3083 | } |
3084 | |
3085 | void * pThis = pRefThis->UnBox(); |
3086 | void * pTarget = pRefTarget->UnBox(); |
3087 | |
3088 | #define CMPEXPR(x1,x2) ((x1) == (x2)) ? 0 : ((x1) < (x2)) ? -1 : 1 |
3089 | |
3090 | switch (pMTThis->GetInternalCorElementType()) { |
3091 | |
3092 | case ELEMENT_TYPE_I1: |
3093 | { |
3094 | INT8 i1 = *(INT8*)pThis; |
3095 | INT8 i2 = *(INT8*)pTarget; |
3096 | |
3097 | return CMPEXPR(i1,i2); |
3098 | } |
3099 | break; |
3100 | |
3101 | case ELEMENT_TYPE_I2: |
3102 | { |
3103 | INT16 i1 = *(INT16*)pThis; |
3104 | INT16 i2 = *(INT16*)pTarget; |
3105 | |
3106 | return CMPEXPR(i1,i2); |
3107 | } |
3108 | break; |
3109 | |
3110 | |
3111 | case ELEMENT_TYPE_I4: |
3112 | IN_WIN32(case ELEMENT_TYPE_I:) |
3113 | { |
3114 | INT32 i1 = *(INT32*)pThis; |
3115 | INT32 i2 = *(INT32*)pTarget; |
3116 | |
3117 | return CMPEXPR(i1,i2); |
3118 | } |
3119 | break; |
3120 | |
3121 | |
3122 | case ELEMENT_TYPE_I8: |
3123 | IN_WIN64(case ELEMENT_TYPE_I:) |
3124 | { |
3125 | INT64 i1 = *(INT64*)pThis; |
3126 | INT64 i2 = *(INT64*)pTarget; |
3127 | |
3128 | return CMPEXPR(i1,i2); |
3129 | } |
3130 | break; |
3131 | |
3132 | case ELEMENT_TYPE_BOOLEAN: |
3133 | { |
3134 | bool b1 = !!*(UINT8 *)pThis; |
3135 | bool b2 = !!*(UINT8 *)pTarget; |
3136 | |
3137 | return CMPEXPR(b1,b2); |
3138 | } |
3139 | break; |
3140 | |
3141 | case ELEMENT_TYPE_U1: |
3142 | { |
3143 | UINT8 u1 = *(UINT8 *)pThis; |
3144 | UINT8 u2 = *(UINT8 *)pTarget; |
3145 | |
3146 | return CMPEXPR(u1,u2); |
3147 | } |
3148 | break; |
3149 | |
3150 | case ELEMENT_TYPE_U2: |
3151 | case ELEMENT_TYPE_CHAR: |
3152 | { |
3153 | UINT16 u1 = *(UINT16 *)pThis; |
3154 | UINT16 u2 = *(UINT16 *)pTarget; |
3155 | |
3156 | return CMPEXPR(u1,u2); |
3157 | } |
3158 | break; |
3159 | |
3160 | case ELEMENT_TYPE_U4: |
3161 | IN_WIN32(case ELEMENT_TYPE_U:) |
3162 | { |
3163 | UINT32 u1 = *(UINT32 *)pThis; |
3164 | UINT32 u2 = *(UINT32 *)pTarget; |
3165 | |
3166 | return CMPEXPR(u1,u2); |
3167 | } |
3168 | break; |
3169 | |
3170 | case ELEMENT_TYPE_U8: |
3171 | IN_WIN64(case ELEMENT_TYPE_U:) |
3172 | { |
3173 | UINT64 u1 = *(UINT64*)pThis; |
3174 | UINT64 u2 = *(UINT64*)pTarget; |
3175 | |
3176 | return CMPEXPR(u1,u2); |
3177 | } |
3178 | break; |
3179 | |
3180 | case ELEMENT_TYPE_R4: |
3181 | { |
3182 | static_assert_no_msg(sizeof(float) == 4); |
3183 | |
3184 | float f1 = *(float*)pThis; |
3185 | float f2 = *(float*)pTarget; |
3186 | |
3187 | return CMPEXPR(f1,f2); |
3188 | } |
3189 | break; |
3190 | |
3191 | case ELEMENT_TYPE_R8: |
3192 | { |
3193 | static_assert_no_msg(sizeof(double) == 8); |
3194 | |
3195 | double d1 = *(double*)pThis; |
3196 | double d2 = *(double*)pTarget; |
3197 | |
3198 | return CMPEXPR(d1,d2); |
3199 | } |
3200 | break; |
3201 | |
3202 | default: |
3203 | break; |
3204 | } |
3205 | |
3206 | return retInvalidEnumType; // second error case -- unsupported enum type |
3207 | } |
3208 | FCIMPLEND |
3209 | |
3210 | |