xref: /aosp_15_r20/external/cronet/base/win/scoped_variant.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2010 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/win/scoped_variant.h"
6 
7 #include <wrl/client.h>
8 
9 #include <algorithm>
10 #include <functional>
11 
12 #include "base/check.h"
13 #include "base/logging.h"
14 #include "base/win/propvarutil.h"
15 #include "base/win/variant_conversions.h"
16 
17 namespace base {
18 namespace win {
19 
20 // Global, const instance of an empty variant.
21 const VARIANT ScopedVariant::kEmptyVariant = {{{VT_EMPTY}}};
22 
ScopedVariant(ScopedVariant && var)23 ScopedVariant::ScopedVariant(ScopedVariant&& var) {
24   var_.vt = VT_EMPTY;
25   Reset(var.Release());
26 }
27 
~ScopedVariant()28 ScopedVariant::~ScopedVariant() {
29   static_assert(sizeof(ScopedVariant) == sizeof(VARIANT), "ScopedVariantSize");
30   ::VariantClear(&var_);
31 }
32 
ScopedVariant(const wchar_t * str)33 ScopedVariant::ScopedVariant(const wchar_t* str) {
34   var_.vt = VT_EMPTY;
35   Set(str);
36 }
37 
ScopedVariant(const wchar_t * str,UINT length)38 ScopedVariant::ScopedVariant(const wchar_t* str, UINT length) {
39   var_.vt = VT_BSTR;
40   var_.bstrVal = ::SysAllocStringLen(str, length);
41 }
42 
ScopedVariant(long value,VARTYPE vt)43 ScopedVariant::ScopedVariant(long value, VARTYPE vt) {  // NOLINT(runtime/int)
44   var_.vt = vt;
45   var_.lVal = value;
46 }
47 
ScopedVariant(int value)48 ScopedVariant::ScopedVariant(int value) {
49   var_.vt = VT_I4;
50   var_.lVal = value;
51 }
52 
ScopedVariant(bool value)53 ScopedVariant::ScopedVariant(bool value) {
54   var_.vt = VT_BOOL;
55   var_.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE;
56 }
57 
ScopedVariant(double value,VARTYPE vt)58 ScopedVariant::ScopedVariant(double value, VARTYPE vt) {
59   DCHECK(vt == VT_R8 || vt == VT_DATE);
60   var_.vt = vt;
61   var_.dblVal = value;
62 }
63 
ScopedVariant(IDispatch * dispatch)64 ScopedVariant::ScopedVariant(IDispatch* dispatch) {
65   var_.vt = VT_EMPTY;
66   Set(dispatch);
67 }
68 
ScopedVariant(IUnknown * unknown)69 ScopedVariant::ScopedVariant(IUnknown* unknown) {
70   var_.vt = VT_EMPTY;
71   Set(unknown);
72 }
73 
ScopedVariant(SAFEARRAY * safearray)74 ScopedVariant::ScopedVariant(SAFEARRAY* safearray) {
75   var_.vt = VT_EMPTY;
76   Set(safearray);
77 }
78 
ScopedVariant(const VARIANT & var)79 ScopedVariant::ScopedVariant(const VARIANT& var) {
80   var_.vt = VT_EMPTY;
81   Set(var);
82 }
83 
Reset(const VARIANT & var)84 void ScopedVariant::Reset(const VARIANT& var) {
85   if (&var != &var_) {
86     ::VariantClear(&var_);
87     var_ = var;
88   }
89 }
90 
Release()91 VARIANT ScopedVariant::Release() {
92   VARIANT var = var_;
93   var_.vt = VT_EMPTY;
94   return var;
95 }
96 
Swap(ScopedVariant & var)97 void ScopedVariant::Swap(ScopedVariant& var) {
98   VARIANT tmp = var_;
99   var_ = var.var_;
100   var.var_ = tmp;
101 }
102 
Receive()103 VARIANT* ScopedVariant::Receive() {
104   DCHECK(!IsLeakableVarType(var_.vt)) << "variant leak. type: " << var_.vt;
105   return &var_;
106 }
107 
Copy() const108 VARIANT ScopedVariant::Copy() const {
109   VARIANT ret = {{{VT_EMPTY}}};
110   ::VariantCopy(&ret, &var_);
111   return ret;
112 }
113 
Compare(const VARIANT & other,bool ignore_case) const114 int ScopedVariant::Compare(const VARIANT& other, bool ignore_case) const {
115   DCHECK(!V_ISARRAY(&var_))
116       << "Comparison is not supported when |this| owns a SAFEARRAY";
117   DCHECK(!V_ISARRAY(&other))
118       << "Comparison is not supported when |other| owns a SAFEARRAY";
119 
120   const bool this_is_empty = var_.vt == VT_EMPTY || var_.vt == VT_NULL;
121   const bool other_is_empty = other.vt == VT_EMPTY || other.vt == VT_NULL;
122 
123   // 1. VT_NULL and VT_EMPTY is always considered less-than any other VARTYPE.
124   if (this_is_empty)
125     return other_is_empty ? 0 : -1;
126   if (other_is_empty)
127     return 1;
128 
129   // 2. If both VARIANTS have either VT_UNKNOWN or VT_DISPATCH even if the
130   //    VARTYPEs do not match, the address of its IID_IUnknown is compared to
131   //    guarantee a logical ordering even though it is not a meaningful order.
132   //    e.g. (a.Compare(b) != b.Compare(a)) unless (a == b).
133   const bool this_is_unknown = var_.vt == VT_UNKNOWN || var_.vt == VT_DISPATCH;
134   const bool other_is_unknown =
135       other.vt == VT_UNKNOWN || other.vt == VT_DISPATCH;
136   if (this_is_unknown && other_is_unknown) {
137     // https://docs.microsoft.com/en-us/windows/win32/com/rules-for-implementing-queryinterface
138     // Query IID_IUnknown to determine whether the two variants point
139     // to the same instance of an object
140     Microsoft::WRL::ComPtr<IUnknown> this_unknown;
141     Microsoft::WRL::ComPtr<IUnknown> other_unknown;
142     V_UNKNOWN(&var_)->QueryInterface(IID_PPV_ARGS(&this_unknown));
143     V_UNKNOWN(&other)->QueryInterface(IID_PPV_ARGS(&other_unknown));
144     if (this_unknown.Get() == other_unknown.Get())
145       return 0;
146     // std::less for any pointer type yields a strict total order even if the
147     // built-in operator< does not.
148     return std::less<>{}(this_unknown.Get(), other_unknown.Get()) ? -1 : 1;
149   }
150 
151   // 3. If the VARTYPEs do not match, then the value of the VARTYPE is compared.
152   if (V_VT(&var_) != V_VT(&other))
153     return (V_VT(&var_) < V_VT(&other)) ? -1 : 1;
154 
155   const VARTYPE shared_vartype = V_VT(&var_);
156   // 4. Comparing VT_BSTR values is a lexicographical comparison of the contents
157   //    of the BSTR, taking into account |ignore_case|.
158   if (shared_vartype == VT_BSTR) {
159     ULONG flags = ignore_case ? NORM_IGNORECASE : 0;
160     HRESULT hr =
161         ::VarBstrCmp(V_BSTR(&var_), V_BSTR(&other), LOCALE_USER_DEFAULT, flags);
162     DCHECK(SUCCEEDED(hr) && hr != VARCMP_NULL)
163         << "unsupported variant comparison: " << var_.vt << " and " << other.vt;
164 
165     switch (hr) {
166       case VARCMP_LT:
167         return -1;
168       case VARCMP_GT:
169       case VARCMP_NULL:
170         return 1;
171       default:
172         return 0;
173     }
174   }
175 
176   // 5. Otherwise returns the lexicographical comparison of the values held by
177   //    the two VARIANTS that share the same VARTYPE.
178   return ::VariantCompare(var_, other);
179 }
180 
Set(const wchar_t * str)181 void ScopedVariant::Set(const wchar_t* str) {
182   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
183   var_.vt = VT_BSTR;
184   var_.bstrVal = ::SysAllocString(str);
185 }
186 
Set(int8_t i8)187 void ScopedVariant::Set(int8_t i8) {
188   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
189   var_.vt = VT_I1;
190   var_.cVal = i8;
191 }
192 
Set(uint8_t ui8)193 void ScopedVariant::Set(uint8_t ui8) {
194   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
195   var_.vt = VT_UI1;
196   var_.bVal = ui8;
197 }
198 
Set(int16_t i16)199 void ScopedVariant::Set(int16_t i16) {
200   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
201   var_.vt = VT_I2;
202   var_.iVal = i16;
203 }
204 
Set(uint16_t ui16)205 void ScopedVariant::Set(uint16_t ui16) {
206   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
207   var_.vt = VT_UI2;
208   var_.uiVal = ui16;
209 }
210 
Set(int32_t i32)211 void ScopedVariant::Set(int32_t i32) {
212   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
213   var_.vt = VT_I4;
214   var_.lVal = i32;
215 }
216 
Set(uint32_t ui32)217 void ScopedVariant::Set(uint32_t ui32) {
218   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
219   var_.vt = VT_UI4;
220   var_.ulVal = ui32;
221 }
222 
Set(int64_t i64)223 void ScopedVariant::Set(int64_t i64) {
224   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
225   var_.vt = VT_I8;
226   var_.llVal = i64;
227 }
228 
Set(uint64_t ui64)229 void ScopedVariant::Set(uint64_t ui64) {
230   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
231   var_.vt = VT_UI8;
232   var_.ullVal = ui64;
233 }
234 
Set(float r32)235 void ScopedVariant::Set(float r32) {
236   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
237   var_.vt = VT_R4;
238   var_.fltVal = r32;
239 }
240 
Set(double r64)241 void ScopedVariant::Set(double r64) {
242   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
243   var_.vt = VT_R8;
244   var_.dblVal = r64;
245 }
246 
SetDate(DATE date)247 void ScopedVariant::SetDate(DATE date) {
248   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
249   var_.vt = VT_DATE;
250   var_.date = date;
251 }
252 
Set(IDispatch * disp)253 void ScopedVariant::Set(IDispatch* disp) {
254   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
255   var_.vt = VT_DISPATCH;
256   var_.pdispVal = disp;
257   if (disp)
258     disp->AddRef();
259 }
260 
Set(bool b)261 void ScopedVariant::Set(bool b) {
262   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
263   var_.vt = VT_BOOL;
264   var_.boolVal = b ? VARIANT_TRUE : VARIANT_FALSE;
265 }
266 
Set(IUnknown * unk)267 void ScopedVariant::Set(IUnknown* unk) {
268   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
269   var_.vt = VT_UNKNOWN;
270   var_.punkVal = unk;
271   if (unk)
272     unk->AddRef();
273 }
274 
Set(SAFEARRAY * array)275 void ScopedVariant::Set(SAFEARRAY* array) {
276   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
277   if (SUCCEEDED(::SafeArrayGetVartype(array, &var_.vt))) {
278     var_.vt |= VT_ARRAY;
279     var_.parray = array;
280   } else {
281     DCHECK(!array) << "Unable to determine safearray vartype";
282     var_.vt = VT_EMPTY;
283   }
284 }
285 
Set(const VARIANT & var)286 void ScopedVariant::Set(const VARIANT& var) {
287   DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
288   if (FAILED(::VariantCopy(&var_, &var))) {
289     DLOG(ERROR) << "VariantCopy failed";
290     var_.vt = VT_EMPTY;
291   }
292 }
293 
operator =(ScopedVariant && var)294 ScopedVariant& ScopedVariant::operator=(ScopedVariant&& var) {
295   if (var.ptr() != &var_)
296     Reset(var.Release());
297   return *this;
298 }
299 
operator =(const VARIANT & var)300 ScopedVariant& ScopedVariant::operator=(const VARIANT& var) {
301   if (&var != &var_) {
302     VariantClear(&var_);
303     Set(var);
304   }
305   return *this;
306 }
307 
IsLeakableVarType(VARTYPE vt)308 bool ScopedVariant::IsLeakableVarType(VARTYPE vt) {
309   bool leakable = false;
310   switch (vt & VT_TYPEMASK) {
311     case VT_BSTR:
312     case VT_DISPATCH:
313     // we treat VT_VARIANT as leakable to err on the safe side.
314     case VT_VARIANT:
315     case VT_UNKNOWN:
316     case VT_SAFEARRAY:
317 
318     // very rarely used stuff (if ever):
319     case VT_VOID:
320     case VT_PTR:
321     case VT_CARRAY:
322     case VT_USERDEFINED:
323     case VT_LPSTR:
324     case VT_LPWSTR:
325     case VT_RECORD:
326     case VT_INT_PTR:
327     case VT_UINT_PTR:
328     case VT_FILETIME:
329     case VT_BLOB:
330     case VT_STREAM:
331     case VT_STORAGE:
332     case VT_STREAMED_OBJECT:
333     case VT_STORED_OBJECT:
334     case VT_BLOB_OBJECT:
335     case VT_VERSIONED_STREAM:
336     case VT_BSTR_BLOB:
337       leakable = true;
338       break;
339   }
340 
341   if (!leakable && (vt & VT_ARRAY) != 0) {
342     leakable = true;
343   }
344 
345   return leakable;
346 }
347 
348 }  // namespace win
349 }  // namespace base
350