1/*
2TEST_RUN_OUTPUT
3objc\[\d+\]: protocol_addProtocol: added protocol 'EmptyProto' is still under construction!
4objc\[\d+\]: objc_registerProtocol: protocol 'Proto1' was already registered!
5objc\[\d+\]: protocol_addProtocol: modified protocol 'Proto1' is not under construction!
6objc\[\d+\]: protocol_addMethodDescription: protocol 'Proto1' is not under construction!
7objc\[\d+\]: objc_registerProtocol: protocol 'SuperProto' was already registered!
8objc\[\d+\]: protocol_addProtocol: modified protocol 'SuperProto' is not under construction!
9objc\[\d+\]: protocol_addMethodDescription: protocol 'SuperProto' is not under construction!
10OK: addProtocol.m
11END
12*/
13
14#include "test.h"
15
16#include <objc/runtime.h>
17
18@protocol SuperProto @end
19@protocol SuperProto2 @end
20@protocol UnrelatedProto @end
21
22int main()
23{
24    Protocol *proto, *proto2;
25    Protocol * __unsafe_unretained *protolist;
26    struct objc_method_description *desclist;
27    objc_property_t *proplist;
28    unsigned int count;
29
30    // make sure binary contains hard copies of these protocols
31    proto = @protocol(SuperProto);
32    proto = @protocol(SuperProto2);
33
34    // Adding a protocol
35
36    char *name = strdup("Proto1");
37    proto = objc_allocateProtocol(name);
38    testassert(proto);
39    testassert(!objc_getProtocol(name));
40
41    protocol_addProtocol(proto, @protocol(SuperProto));
42    protocol_addProtocol(proto, @protocol(SuperProto2));
43    // no inheritance cycles
44    proto2 = objc_allocateProtocol("EmptyProto");
45    protocol_addProtocol(proto, proto2);  // fails
46    objc_registerProtocol(proto2);
47    protocol_addProtocol(proto, proto2);  // succeeds
48
49    char *types = strdup("@:");
50    protocol_addMethodDescription(proto, @selector(ReqInst0), types, YES, YES);
51    protocol_addMethodDescription(proto, @selector(ReqInst1), types, YES, YES);
52    protocol_addMethodDescription(proto, @selector(ReqInst2), types, YES, YES);
53    protocol_addMethodDescription(proto, @selector(ReqInst3), types, YES, YES);
54
55    protocol_addMethodDescription(proto, @selector(ReqClas0), types, YES, NO);
56    protocol_addMethodDescription(proto, @selector(ReqClas1), types, YES, NO);
57    protocol_addMethodDescription(proto, @selector(ReqClas2), types, YES, NO);
58    protocol_addMethodDescription(proto, @selector(ReqClas3), types, YES, NO);
59
60    protocol_addMethodDescription(proto, @selector(OptInst0), types, NO,  YES);
61    protocol_addMethodDescription(proto, @selector(OptInst1), types, NO,  YES);
62    protocol_addMethodDescription(proto, @selector(OptInst2), types, NO,  YES);
63    protocol_addMethodDescription(proto, @selector(OptInst3), types, NO,  YES);
64
65    protocol_addMethodDescription(proto, @selector(OptClas0), types, NO,  NO);
66    protocol_addMethodDescription(proto, @selector(OptClas1), types, NO,  NO);
67    protocol_addMethodDescription(proto, @selector(OptClas2), types, NO,  NO);
68    protocol_addMethodDescription(proto, @selector(OptClas3), types, NO,  NO);
69
70    char *name0 = strdup("ReqInst0");
71    char *name1 = strdup("ReqInst1");
72    char *name2 = strdup("ReqInst2");
73    char *name3 = strdup("ReqInst3");
74    char *attrname = strdup("T");
75    char *attrvalue = strdup("i");
76    objc_property_attribute_t attrs[] = {{attrname, attrvalue}};
77    int attrcount = sizeof(attrs) / sizeof(attrs[0]);
78    protocol_addProperty(proto, name0, attrs, attrcount, YES, YES);
79    protocol_addProperty(proto, name1, attrs, attrcount, YES, YES);
80    protocol_addProperty(proto, name2, attrs, attrcount, YES, YES);
81    protocol_addProperty(proto, name3, attrs, attrcount, YES, YES);
82
83    objc_registerProtocol(proto);
84    testassert(0 == strcmp(protocol_getName(proto), "Proto1"));
85
86    // Use of added protocols
87
88    testassert(proto == objc_getProtocol("Proto1"));
89    strcpy(name, "XXXXXX");  // name is copied
90    testassert(0 == strcmp(protocol_getName(proto), "Proto1"));
91
92    protolist = protocol_copyProtocolList(proto, &count);
93    testassert(protolist);
94    testassert(count == 3);
95    // note this order is not required
96    testassert(protolist[0] == @protocol(SuperProto)  &&
97               protolist[1] == @protocol(SuperProto2)  &&
98               protolist[2] == proto2);
99    free(protolist);
100
101    testassert(protocol_conformsToProtocol(proto, proto2));
102    testassert(protocol_conformsToProtocol(proto, @protocol(SuperProto)));
103    testassert(!protocol_conformsToProtocol(proto, @protocol(UnrelatedProto)));
104
105    strcpy(types, "XX");  // types is copied
106    desclist = protocol_copyMethodDescriptionList(proto, YES, YES, &count);
107    testassert(desclist  &&  count == 4);
108    testprintf("%p %p\n", desclist[0].name, @selector(ReqInst0));
109    // testassert(desclist[0].name == @selector(ReqInst0));
110    testassert(0 == strcmp(desclist[0].types, "@:"));
111    free(desclist);
112    desclist = protocol_copyMethodDescriptionList(proto, YES, NO,  &count);
113    testassert(desclist  &&  count == 4);
114    testassert(desclist[1].name == @selector(ReqClas1));
115    testassert(0 == strcmp(desclist[1].types, "@:"));
116    free(desclist);
117    desclist = protocol_copyMethodDescriptionList(proto, NO,  YES, &count);
118    testassert(desclist  &&  count == 4);
119    testassert(desclist[2].name == @selector(OptInst2));
120    testassert(0 == strcmp(desclist[2].types, "@:"));
121    free(desclist);
122    desclist = protocol_copyMethodDescriptionList(proto, NO,  NO,  &count);
123    testassert(desclist  &&  count == 4);
124    testassert(desclist[3].name == @selector(OptClas3));
125    testassert(0 == strcmp(desclist[3].types, "@:"));
126    free(desclist);
127
128    strcpy(name0, "XXXXXXXX");  // name is copied
129    strcpy(name1, "XXXXXXXX");  // name is copied
130    strcpy(name2, "XXXXXXXX");  // name is copied
131    strcpy(name3, "XXXXXXXX");  // name is copied
132    strcpy(attrname, "X");             // description is copied
133    strcpy(attrvalue, "X");            // description is copied
134    memset(attrs, 'X', sizeof(attrs)); // description is copied
135    proplist = protocol_copyPropertyList(proto, &count);
136    testassert(proplist);
137    testassert(count == 4);
138    // note this order is not required
139    testassert(0 == strcmp(property_getName(proplist[0]), "ReqInst0"));
140    testassert(0 == strcmp(property_getName(proplist[1]), "ReqInst1"));
141    testassert(0 == strcmp(property_getName(proplist[2]), "ReqInst2"));
142    testassert(0 == strcmp(property_getName(proplist[3]), "ReqInst3"));
143    testassert(0 == strcmp(property_getAttributes(proplist[0]), "Ti"));
144    testassert(0 == strcmp(property_getAttributes(proplist[1]), "Ti"));
145    testassert(0 == strcmp(property_getAttributes(proplist[2]), "Ti"));
146    testassert(0 == strcmp(property_getAttributes(proplist[3]), "Ti"));
147    free(proplist);
148
149
150    testassert(proto2 == objc_getProtocol("EmptyProto"));
151    testassert(0 == strcmp(protocol_getName(proto2), "EmptyProto"));
152
153    protolist = protocol_copyProtocolList(proto2, &count);
154    testassert(!protolist);
155    testassert(count == 0);
156
157    testassert(!protocol_conformsToProtocol(proto2, proto));
158    testassert(!protocol_conformsToProtocol(proto2,@protocol(SuperProto)));
159    testassert(!protocol_conformsToProtocol(proto2,@protocol(UnrelatedProto)));
160
161    desclist = protocol_copyMethodDescriptionList(proto2, YES, YES, &count);
162    testassert(!desclist  &&  count == 0);
163    desclist = protocol_copyMethodDescriptionList(proto2, YES, NO,  &count);
164    testassert(!desclist  &&  count == 0);
165    desclist = protocol_copyMethodDescriptionList(proto2, NO,  YES, &count);
166    testassert(!desclist  &&  count == 0);
167    desclist = protocol_copyMethodDescriptionList(proto2, NO,  NO,  &count);
168    testassert(!desclist  &&  count == 0);
169
170    // Immutability of existing protocols
171
172    objc_registerProtocol(proto);
173    protocol_addProtocol(proto, @protocol(SuperProto2));
174    protocol_addMethodDescription(proto, @selector(foo), "", YES, YES);
175
176    objc_registerProtocol(@protocol(SuperProto));
177    protocol_addProtocol(@protocol(SuperProto), @protocol(SuperProto2));
178    protocol_addMethodDescription(@protocol(SuperProto), @selector(foo), "", YES, YES);
179
180    // No duplicates
181
182    proto = objc_allocateProtocol("SuperProto");
183    testassert(!proto);
184    proto = objc_allocateProtocol("Proto1");
185    testassert(!proto);
186
187    // NULL protocols ignored
188
189    protocol_addProtocol((Protocol *)objc_unretainedObject((void*)1), NULL);
190    protocol_addProtocol(NULL, (Protocol *)objc_unretainedObject((void*)1));
191    protocol_addProtocol(NULL, NULL);
192    protocol_addMethodDescription(NULL, @selector(foo), "", YES, YES);
193
194    succeed(__FILE__);
195}
196