Use bucket parameter in GetIfChanged for support binaries.
[chromium-blink-merge.git] / third_party / ocmock / OCMock / OCMBoxedReturnValueProvider.mm
blobcf59610344e39efd15c622124356391c2dc490d4
1 //---------------------------------------------------------------------------------------
2 //  $Id$
3 //  Copyright (c) 2009 by Mulle Kybernetik. See License file for details.
4 //---------------------------------------------------------------------------------------
6 #import "OCMBoxedReturnValueProvider.h"
7 #import <objc/runtime.h>
9 #if defined(__clang__)
10 #include <vector>  // for _LIBCPP_ABI_VERSION to detect if using libc++
11 #endif
13 #if defined(__clang__) && defined(_LIBCPP_ABI_VERSION)
14 namespace {
15 // Default stack size to use when checking for matching opening and closing
16 // characters (<> and {}). This is used to reduce the number of allocations
17 // in AdvanceTypeDescriptionPointer function.
18 const size_t kDefaultStackSize = 32;
20 // Move to the next pertinent character in a type description. This skips
21 // all the field expansion that clang includes in the type description when
22 // compiling with libc++.
24 // See inner comment of -isValueTypeCompatibleWithInvocation: for more details.
25 // Returns true if the pointer was advanced, false if the type description was
26 // not correctly parsed.
27 bool AdvanceTypeDescriptionPointer(const char *&typeDescription) {
28         if (!*typeDescription)
29                 return true;
31         ++typeDescription;
32         if (*typeDescription != '=')
33                 return true;
35         ++typeDescription;
36         std::vector<char> stack;
37         stack.reserve(kDefaultStackSize);
38         while (*typeDescription) {
39                 const char current = *typeDescription;
40                 if (current == '<' || current == '{') {
41                         stack.push_back(current);
42                 } else if (current == '>' || current == '}') {
43                         if (!stack.empty()) {
44                                 const char opening = stack.back();
45                                 if ((opening == '<' && current != '>') ||
46                                                 (opening == '{' && current != '}')) {
47                                         return false;
48                                 }
49                                 stack.pop_back();
50                         } else {
51                                 return current == '}';
52                         }
53                 } else if (current == ',' && stack.empty()) {
54                         return true;
55                 }
56                 ++typeDescription;
57         }
58         return true;
61 #endif  // defined(__clang__) && defined(_LIBCPP_ABI_VERSION)
63 @interface OCMBoxedReturnValueProvider ()
65 - (BOOL)isValueTypeCompatibleWithInvocation:(NSInvocation *)anInvocation;
67 @end
69 @implementation OCMBoxedReturnValueProvider
71 - (BOOL)isValueTypeCompatibleWithInvocation:(NSInvocation *)anInvocation {
72         const char *returnType = [[anInvocation methodSignature] methodReturnType];
73         const char *valueType = [(NSValue *)returnValue objCType];
75 #if defined(__aarch64__) || defined(__x86_64__)
76         // ARM64 uses 'B' for BOOLs in method signature but 'c' in NSValue. That case
77         // should match.
78         if (strcmp(returnType, "B") == 0 && strcmp(valueType, "c") == 0)
79                 return YES;
80 #endif  // defined(__aarch64__) || defined(__x86_64__)
82 #if defined(__clang__) && defined(_LIBCPP_ABI_VERSION)
83         // The type representation of the return type of the invocation, and the
84         // type representation passed to NSValue are not the same for C++ objects
85         // when compiling with libc++ with clang.
86         //
87         // In that configuration, the C++ class are expanded to list the types of
88         // the fields, but the depth of the expansion for templated types is larger
89         // for the value stored in the NSValue.
90         //
91         // For example, when creating a OCMOCK_VALUE with a GURL object (from the
92         // Chromium project), then the two types representations are:
93         //
94         // r^{GURL={basic_string<char, std::__1::char_traits<char>, std::__1::alloca
95         // tor<char> >={__compressed_pair<std::__1::basic_string<char, std::__1::cha
96         // r_traits<char>, std::__1::allocator<char> >::__rep, std::__1::allocator<c
97         // har> >={__rep}}}B{Parsed={Component=ii}{Component=ii}{Component=ii}{Compo
98         // nent=ii}{Component=ii}{Component=ii}{Component=ii}{Component=ii}^{Parsed}
99         // }{scoped_ptr<GURL, base::DefaultDeleter<GURL> >={scoped_ptr_impl<GURL, ba
100         // se::DefaultDeleter<GURL> >={Data=^{GURL}}}}}
101         //
102         // r^{GURL={basic_string<char, std::__1::char_traits<char>, std::__1::alloca
103         // tor<char> >={__compressed_pair<std::__1::basic_string<char, std::__1::cha
104         // r_traits<char>, std::__1::allocator<char> >::__rep, std::__1::allocator<c
105         // har> >={__rep=(?={__long=II*}{__short=(?=Cc)[11c]}{__raw=[3L]})}}}B{Parse
106         // d={Component=ii}{Component=ii}{Component=ii}{Component=ii}{Component=ii}{
107         // Component=ii}{Component=ii}{Component=ii}^{Parsed}}{scoped_ptr<GURL, base
108         // ::DefaultDeleter<GURL> >={scoped_ptr_impl<GURL, base::DefaultDeleter<GURL
109         // > >={Data=^{GURL}}}}}
110         //
111         // Since those types should be considered equals, we un-expand them during
112         // the comparison. For that, we remove everything following an "=" until we
113         // meet a non-matched "}" or a ",".
115         while (*returnType && *valueType) {
116                 if (*returnType != *valueType)
117                         return NO;
119                 if (!AdvanceTypeDescriptionPointer(returnType))
120                         return NO;
122                 if (!AdvanceTypeDescriptionPointer(valueType))
123                         return NO;
124         }
126         return !*returnType && !*valueType;
127 #else
128         return strcmp(returnType, valueType) == 0;
129 #endif  // defined(__clang__) && defined(_LIBCPP_ABI_VERSION)
132 - (void)handleInvocation:(NSInvocation *)anInvocation
134         if (![self isValueTypeCompatibleWithInvocation:anInvocation]) {
135                 const char *returnType = [[anInvocation methodSignature] methodReturnType];
136                 const char *valueType = [(NSValue *)returnValue objCType];
137                 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"Return value does not match method signature; signature declares '%s' but value is '%s'.", returnType, valueType] userInfo:nil];
138         }
139         void *buffer = malloc([[anInvocation methodSignature] methodReturnLength]);
140         [returnValue getValue:buffer];
141         [anInvocation setReturnValue:buffer];
142         free(buffer);
145 @end