jscript: Return 'unknown' in typeof operator for native object's values that can...
[wine] / dlls / oleaut32 / recinfo.c
1 /*
2  * Copyright 2005 Jacek Caban
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include <stdarg.h>
20
21 #define COBJMACROS
22 #define NONAMELESSUNION
23 #define NONAMELESSSTRUCT
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "objbase.h"
28 #include "oaidl.h"
29 #include "oleauto.h"
30
31 #include "wine/unicode.h"
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(ole);
35
36 typedef struct {
37     enum VARENUM vt;
38     VARKIND varkind;
39     ULONG offset;
40     BSTR name;
41 } fieldstr;
42
43 typedef struct {
44     IRecordInfo IRecordInfo_iface;
45     LONG ref;
46
47     GUID guid;
48     UINT lib_index;
49     WORD n_vars;
50     ULONG size;
51     BSTR name;
52     fieldstr *fields;
53     ITypeInfo *pTypeInfo;
54 } IRecordInfoImpl;
55
56 static inline IRecordInfoImpl *impl_from_IRecordInfo(IRecordInfo *iface)
57 {
58     return CONTAINING_RECORD(iface, IRecordInfoImpl, IRecordInfo_iface);
59 }
60
61 static HRESULT copy_to_variant(void *src, VARIANT *pvar, enum VARENUM vt)
62 {
63     TRACE("%p %p %d\n", src, pvar, vt);
64
65 #define CASE_COPY(x) \
66     case VT_ ## x: \
67         memcpy(&V_ ## x(pvar), src, sizeof(V_ ## x(pvar))); \
68         break 
69
70     switch(vt) {
71         CASE_COPY(I2);
72         CASE_COPY(I4);
73         CASE_COPY(R4);
74         CASE_COPY(R8);
75         CASE_COPY(CY);
76         CASE_COPY(DATE);
77         CASE_COPY(BSTR);
78         CASE_COPY(ERROR);
79         CASE_COPY(BOOL);
80         CASE_COPY(DECIMAL);
81         CASE_COPY(I1);
82         CASE_COPY(UI1);
83         CASE_COPY(UI2);
84         CASE_COPY(UI4);
85         CASE_COPY(I8);
86         CASE_COPY(UI8);
87         CASE_COPY(INT);
88         CASE_COPY(UINT);
89         CASE_COPY(INT_PTR);
90         CASE_COPY(UINT_PTR);
91     default:
92         FIXME("Not supported type: %d\n", vt);
93         return E_NOTIMPL;
94     };
95 #undef CASE_COPY
96
97     V_VT(pvar) = vt;
98     return S_OK;
99 }
100
101 static HRESULT copy_from_variant(VARIANT *src, void *dest, enum VARENUM vt)
102 {
103     VARIANT var;
104     HRESULT hres;
105
106     TRACE("(%p(%d) %p %d)\n", src, V_VT(src), dest, vt);
107
108     hres = VariantChangeType(&var, src, 0, vt);
109     if(FAILED(hres))
110         return hres;
111
112 #define CASE_COPY(x) \
113     case VT_ ## x: \
114         memcpy(dest, &V_ ## x(&var), sizeof(V_ ## x(&var))); \
115         break
116
117     switch(vt) {
118         CASE_COPY(I2);
119         CASE_COPY(I4);
120         CASE_COPY(R4);
121         CASE_COPY(R8);
122         CASE_COPY(CY);
123         CASE_COPY(DATE);
124         CASE_COPY(BSTR);
125         CASE_COPY(ERROR);
126         CASE_COPY(BOOL);
127         CASE_COPY(DECIMAL);
128         CASE_COPY(I1);
129         CASE_COPY(UI1);
130         CASE_COPY(UI2);
131         CASE_COPY(UI4);
132         CASE_COPY(I8);
133         CASE_COPY(UI8);
134         CASE_COPY(INT);
135         CASE_COPY(UINT);
136         CASE_COPY(INT_PTR);
137         CASE_COPY(UINT_PTR);
138     default:
139         FIXME("Not supported type: %d\n", V_VT(&var));
140         return E_NOTIMPL;
141     };
142 #undef CASE_COPY
143     return S_OK;
144 }
145
146 static HRESULT WINAPI IRecordInfoImpl_QueryInterface(IRecordInfo *iface, REFIID riid,
147                                                 void **ppvObject)
148 {
149     TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppvObject);
150
151     if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IRecordInfo, riid)) {
152        *ppvObject = iface;
153        IRecordInfo_AddRef(iface);
154        return S_OK;
155     }
156
157     FIXME("Not supported interface: %s\n", debugstr_guid(riid));
158     return E_NOINTERFACE;
159 }
160
161 static ULONG WINAPI IRecordInfoImpl_AddRef(IRecordInfo *iface)
162 {
163     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
164     ULONG ref = InterlockedIncrement(&This->ref);
165     TRACE("(%p) -> %d\n", This, ref);
166     return ref;
167 }
168
169 static ULONG WINAPI IRecordInfoImpl_Release(IRecordInfo *iface)
170 {
171     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
172     ULONG ref = InterlockedDecrement(&This->ref);
173
174     TRACE("(%p) -> %d\n", This, ref);
175
176     if(!ref) {
177         int i;
178         for(i=0; i<This->n_vars; i++)
179             SysFreeString(This->fields[i].name);
180         HeapFree(GetProcessHeap(), 0, This->name);
181         HeapFree(GetProcessHeap(), 0, This->fields);
182         ITypeInfo_Release(This->pTypeInfo);
183         HeapFree(GetProcessHeap(), 0, This);
184     }
185     return ref;
186 }
187
188 static HRESULT WINAPI IRecordInfoImpl_RecordInit(IRecordInfo *iface, PVOID pvNew)
189 {
190     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
191     TRACE("(%p)->(%p)\n", This, pvNew);
192
193     if(!pvNew)
194         return E_INVALIDARG;
195
196     memset(pvNew, 0, This->size);
197     return S_OK;
198 }
199
200 static HRESULT WINAPI IRecordInfoImpl_RecordClear(IRecordInfo *iface, PVOID pvExisting)
201 {
202     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
203     int i;
204     PVOID var;
205
206     TRACE("(%p)->(%p)\n", This, pvExisting);
207
208     if(!pvExisting)
209         return E_INVALIDARG;
210
211     for(i=0; i<This->n_vars; i++) {
212         if(This->fields[i].varkind != VAR_PERINSTANCE) {
213             ERR("varkind != VAR_PERINSTANCE\n");
214             continue;
215         }
216         var = ((PBYTE)pvExisting)+This->fields[i].offset;
217         switch(This->fields[i].vt) {
218             case VT_BSTR:
219                SysFreeString(*(BSTR*)var);
220                 *(BSTR*)var = NULL;
221                 break;
222             case VT_I2:
223             case VT_I4:
224             case VT_R4:
225             case VT_R8:
226             case VT_CY:
227             case VT_DATE:
228             case VT_ERROR:
229             case VT_BOOL:
230             case VT_DECIMAL:
231             case VT_I1:
232             case VT_UI1:
233             case VT_UI2:
234             case VT_UI4:
235             case VT_I8:
236             case VT_UI8:
237             case VT_INT:
238             case VT_UINT:
239                 break;
240             case VT_INT_PTR:
241             case VT_UINT_PTR:
242                 *(void**)var = NULL;
243                 break;
244             case VT_SAFEARRAY:
245                 SafeArrayDestroy(var);
246                 break;
247             default:
248                 FIXME("Not supported vt = %d\n", This->fields[i].vt);
249                 break;
250         }
251     }
252     
253     return S_OK;
254 }
255
256 static HRESULT WINAPI IRecordInfoImpl_RecordCopy(IRecordInfo *iface, PVOID pvExisting,
257                                                 PVOID pvNew)
258 {
259     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
260
261     TRACE("(%p)->(%p %p)\n", This, pvExisting, pvNew);
262     
263     if(!pvExisting || !pvNew)
264         return E_INVALIDARG;
265
266     memcpy(pvExisting, pvNew, This->size);
267     return S_OK;
268 }
269
270 static HRESULT WINAPI IRecordInfoImpl_GetGuid(IRecordInfo *iface, GUID *pguid)
271 {
272     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
273
274     TRACE("(%p)->(%p)\n", This, pguid);
275
276     if(!pguid)
277         return E_INVALIDARG;
278
279     *pguid = This->guid;
280     return S_OK;
281 }
282
283 static HRESULT WINAPI IRecordInfoImpl_GetName(IRecordInfo *iface, BSTR *pbstrName)
284 {
285     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
286
287     TRACE("(%p)->(%p)\n", This, pbstrName);
288
289     if(!pbstrName)
290         return E_INVALIDARG;
291
292     *pbstrName = SysAllocString(This->name);
293     return S_OK;
294 }
295
296 static HRESULT WINAPI IRecordInfoImpl_GetSize(IRecordInfo *iface, ULONG *pcbSize)
297 {
298     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
299
300     TRACE("(%p)->(%p)\n", This, pcbSize);
301
302     if(!pcbSize)
303         return E_INVALIDARG;
304     
305     *pcbSize = This->size;
306     return S_OK;
307 }
308
309 static HRESULT WINAPI IRecordInfoImpl_GetTypeInfo(IRecordInfo *iface, ITypeInfo **ppTypeInfo)
310 {
311     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
312
313     TRACE("(%p)->(%p)\n", This, ppTypeInfo);
314
315     if(!ppTypeInfo)
316         return E_INVALIDARG;
317
318     ITypeInfo_AddRef(This->pTypeInfo);
319     *ppTypeInfo = This->pTypeInfo;
320
321     return S_OK;
322 }
323
324 static HRESULT WINAPI IRecordInfoImpl_GetField(IRecordInfo *iface, PVOID pvData,
325                                                 LPCOLESTR szFieldName, VARIANT *pvarField)
326 {
327     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
328     int i;
329
330     TRACE("(%p)->(%p %s %p)\n", This, pvData, debugstr_w(szFieldName), pvarField);
331
332     if(!pvData || !szFieldName || !pvarField)
333         return E_INVALIDARG;
334
335     for(i=0; i<This->n_vars; i++)
336         if(!strcmpW(This->fields[i].name, szFieldName))
337             break;
338     if(i == This->n_vars)
339         return TYPE_E_FIELDNOTFOUND;
340     
341     VariantClear(pvarField);
342     return copy_to_variant(((PBYTE)pvData)+This->fields[i].offset, pvarField,
343             This->fields[i].vt);
344 }
345
346 static HRESULT WINAPI IRecordInfoImpl_GetFieldNoCopy(IRecordInfo *iface, PVOID pvData,
347                             LPCOLESTR szFieldName, VARIANT *pvarField, PVOID *ppvDataCArray)
348 {
349     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
350     int i;
351
352     TRACE("(%p)->(%p %s %p %p)\n", This, pvData, debugstr_w(szFieldName), pvarField, ppvDataCArray);
353
354     if(!pvData || !szFieldName || !pvarField)
355         return E_INVALIDARG;
356
357     for(i=0; i<This->n_vars; i++)
358         if(!strcmpW(This->fields[i].name, szFieldName))
359             break;
360     if(i == This->n_vars)
361         return TYPE_E_FIELDNOTFOUND;
362
363     VariantClear(pvarField);
364     V_VT(pvarField) = VT_BYREF|This->fields[i].vt;
365     V_BYREF(pvarField) = ((PBYTE)pvData)+This->fields[i].offset;
366     *ppvDataCArray = NULL;
367     return S_OK;
368 }
369
370 static HRESULT WINAPI IRecordInfoImpl_PutField(IRecordInfo *iface, ULONG wFlags, PVOID pvData,
371                                             LPCOLESTR szFieldName, VARIANT *pvarField)
372 {
373     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
374     int i;
375
376     TRACE("(%p)->(%08x %p %s %p)\n", This, wFlags, pvData, debugstr_w(szFieldName),
377                                     pvarField);
378
379     if(!pvData || !szFieldName || !pvarField
380             || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
381         return E_INVALIDARG;
382
383     if(wFlags == INVOKE_PROPERTYPUTREF) {
384         FIXME("wFlag == INVOKE_PROPERTYPUTREF not supported\n");
385         return E_NOTIMPL;
386     }
387
388     for(i=0; i<This->n_vars; i++)
389         if(!strcmpW(This->fields[i].name, szFieldName))
390             break;
391     if(i == This->n_vars)
392         return TYPE_E_FIELDNOTFOUND;
393
394     return copy_from_variant(pvarField, ((PBYTE)pvData)+This->fields[i].offset,
395             This->fields[i].vt);
396 }
397
398 static HRESULT WINAPI IRecordInfoImpl_PutFieldNoCopy(IRecordInfo *iface, ULONG wFlags,
399                 PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField)
400 {
401     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
402     int i;
403
404     FIXME("(%p)->(%08x %p %s %p) stub\n", This, wFlags, pvData, debugstr_w(szFieldName), pvarField);
405
406     if(!pvData || !szFieldName || !pvarField
407             || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
408         return E_INVALIDARG;
409
410     for(i=0; i<This->n_vars; i++)
411         if(!strcmpW(This->fields[i].name, szFieldName))
412             break;
413     if(i == This->n_vars)
414         return TYPE_E_FIELDNOTFOUND;
415
416     return E_NOTIMPL;
417 }
418
419 static HRESULT WINAPI IRecordInfoImpl_GetFieldNames(IRecordInfo *iface, ULONG *pcNames,
420                                                 BSTR *rgBstrNames)
421 {
422     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
423     ULONG n = This->n_vars, i;
424
425     TRACE("(%p)->(%p %p)\n", This, pcNames, rgBstrNames);
426
427     if(!pcNames)
428         return E_INVALIDARG;
429
430     if(*pcNames < n)
431         n =  *pcNames;
432
433     if(rgBstrNames) {
434         for(i=0; i<n; i++)
435             rgBstrNames[i] = SysAllocString(This->fields[i].name);
436     }
437     
438     *pcNames = n;
439     return S_OK;
440 }
441
442 static BOOL WINAPI IRecordInfoImpl_IsMatchingType(IRecordInfo *iface, IRecordInfo *pRecordInfo)
443 {
444     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
445
446     FIXME("(%p)->(%p) stub\n", This, pRecordInfo);
447
448     return FALSE;
449 }
450
451 static PVOID WINAPI IRecordInfoImpl_RecordCreate(IRecordInfo *iface)
452 {
453     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
454
455     TRACE("(%p)\n", This);
456
457     return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->size);
458 }
459
460 static HRESULT WINAPI IRecordInfoImpl_RecordCreateCopy(IRecordInfo *iface, PVOID pvSource,
461                                                     PVOID *ppvDest)
462 {
463     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
464
465     TRACE("(%p)->(%p %p)\n", This, pvSource, ppvDest);
466
467     if(!pvSource || !ppvDest)
468         return E_INVALIDARG;
469     
470     *ppvDest = IRecordInfo_RecordCreate(iface);
471     return IRecordInfo_RecordCopy(iface, pvSource, *ppvDest);
472 }
473
474 static HRESULT WINAPI IRecordInfoImpl_RecordDestroy(IRecordInfo *iface, PVOID pvRecord)
475 {
476     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
477     HRESULT hres;
478
479     TRACE("(%p)->(%p)\n", This, pvRecord);
480
481     hres = IRecordInfo_RecordClear(iface, pvRecord);
482     if(FAILED(hres))
483         return hres;
484
485     if(!HeapFree(GetProcessHeap(), 0, pvRecord))
486         return E_INVALIDARG;
487
488     return S_OK;
489 }
490
491 static const IRecordInfoVtbl IRecordInfoImplVtbl = {
492     IRecordInfoImpl_QueryInterface,
493     IRecordInfoImpl_AddRef,
494     IRecordInfoImpl_Release,
495     IRecordInfoImpl_RecordInit,
496     IRecordInfoImpl_RecordClear,
497     IRecordInfoImpl_RecordCopy,
498     IRecordInfoImpl_GetGuid,
499     IRecordInfoImpl_GetName,
500     IRecordInfoImpl_GetSize,
501     IRecordInfoImpl_GetTypeInfo,
502     IRecordInfoImpl_GetField,
503     IRecordInfoImpl_GetFieldNoCopy,
504     IRecordInfoImpl_PutField,
505     IRecordInfoImpl_PutFieldNoCopy,
506     IRecordInfoImpl_GetFieldNames,
507     IRecordInfoImpl_IsMatchingType,
508     IRecordInfoImpl_RecordCreate,
509     IRecordInfoImpl_RecordCreateCopy,
510     IRecordInfoImpl_RecordDestroy
511 };
512
513 /******************************************************************************
514  *      GetRecordInfoFromGuids  [OLEAUT32.322]
515  *
516  * RETURNS
517  *  Success: S_OK
518  *  Failure: E_INVALIDARG, if any argument is invalid.
519  */
520 HRESULT WINAPI GetRecordInfoFromGuids(REFGUID rGuidTypeLib, ULONG uVerMajor,
521                         ULONG uVerMinor, LCID lcid, REFGUID rGuidTypeInfo, IRecordInfo** ppRecInfo)
522 {
523     ITypeInfo *pTypeInfo;
524     ITypeLib *pTypeLib;
525     HRESULT hres;
526     
527     TRACE("(%p,%d,%d,%d,%p,%p)\n", rGuidTypeLib, uVerMajor, uVerMinor,
528             lcid, rGuidTypeInfo, ppRecInfo);
529
530     hres = LoadRegTypeLib(rGuidTypeLib, uVerMajor, uVerMinor, lcid, &pTypeLib);
531     if(FAILED(hres)) {
532         WARN("LoadRegTypeLib failed!\n");
533         return hres;
534     }
535
536     hres = ITypeLib_GetTypeInfoOfGuid(pTypeLib, rGuidTypeInfo, &pTypeInfo);
537     ITypeLib_Release(pTypeLib);
538     if(FAILED(hres)) {
539         WARN("GetTypeInfoOfGuid failed!\n");
540         return hres;
541     }
542
543     hres = GetRecordInfoFromTypeInfo(pTypeInfo, ppRecInfo);
544     ITypeInfo_Release(pTypeInfo);
545     return hres;
546 }
547
548 /******************************************************************************
549  *      GetRecordInfoFromTypeInfo [OLEAUT32.332]
550  */
551 HRESULT WINAPI GetRecordInfoFromTypeInfo(ITypeInfo* pTI, IRecordInfo** ppRecInfo) {
552     HRESULT hres;
553     TYPEATTR *typeattr;
554     IRecordInfoImpl *ret;
555     ITypeInfo *pTypeInfo;
556     int i;
557     GUID guid;
558
559     TRACE("(%p %p)\n", pTI, ppRecInfo);
560
561     if(!pTI || !ppRecInfo)
562         return E_INVALIDARG;
563     
564     hres = ITypeInfo_GetTypeAttr(pTI, &typeattr);
565     if(FAILED(hres) || !typeattr) {
566         WARN("GetTypeAttr failed: %08x\n", hres);
567         return hres;
568     }
569
570     if(typeattr->typekind == TKIND_ALIAS) {
571         hres = ITypeInfo_GetRefTypeInfo(pTI, typeattr->tdescAlias.u.hreftype, &pTypeInfo);
572         guid = typeattr->guid;
573         ITypeInfo_ReleaseTypeAttr(pTI, typeattr);
574         if(FAILED(hres)) {
575             WARN("GetRefTypeInfo failed: %08x\n", hres);
576             return hres;
577         }
578         ITypeInfo_GetTypeAttr(pTypeInfo, &typeattr);
579     }else  {
580         pTypeInfo = pTI;
581         ITypeInfo_AddRef(pTypeInfo);
582         guid = typeattr->guid;
583     }
584
585     if(typeattr->typekind != TKIND_RECORD) {
586         WARN("typekind != TKIND_RECORD\n");
587         ITypeInfo_ReleaseTypeAttr(pTypeInfo, typeattr);
588         ITypeInfo_Release(pTypeInfo);
589         return E_INVALIDARG;
590     }
591
592     ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret));
593     ret->IRecordInfo_iface.lpVtbl = &IRecordInfoImplVtbl;
594     ret->ref = 1;
595     ret->pTypeInfo = pTypeInfo;
596     ret->n_vars = typeattr->cVars;
597     ret->size = typeattr->cbSizeInstance;
598     ITypeInfo_ReleaseTypeAttr(pTypeInfo, typeattr);
599
600     ret->guid = guid;
601
602     /* NOTE: Windows implementation calls ITypeInfo::GetCantainingTypeLib and
603      *       ITypeLib::GetLibAttr, but we currently don't need this.
604      */
605
606     hres = ITypeInfo_GetDocumentation(pTypeInfo, MEMBERID_NIL, &ret->name, NULL, NULL, NULL);
607     if(FAILED(hres)) {
608         WARN("ITypeInfo::GetDocumentation failed\n");
609         ret->name = NULL;
610     }
611
612     ret->fields = HeapAlloc(GetProcessHeap(), 0, ret->n_vars*sizeof(VARDESC));
613     for(i = 0; i<ret->n_vars; i++) {
614         VARDESC *vardesc;
615         hres = ITypeInfo_GetVarDesc(pTypeInfo, i, &vardesc);
616         if(FAILED(hres)) {
617             WARN("GetVarDesc failed\n");
618             continue;
619         }
620         ret->fields[i].vt = vardesc->elemdescVar.tdesc.vt;
621         ret->fields[i].varkind = vardesc->varkind;
622         ret->fields[i].offset = vardesc->u.oInst;
623         hres = ITypeInfo_GetDocumentation(pTypeInfo, vardesc->memid, &ret->fields[i].name,
624                 NULL, NULL, NULL);
625         if(FAILED(hres))
626             WARN("GetDocumentation failed: %08x\n", hres);
627         ITypeInfo_ReleaseVarDesc(pTypeInfo, vardesc);
628     }
629
630     *ppRecInfo = &ret->IRecordInfo_iface;
631
632     return S_OK;
633 }