[Cocoa] Adapt more WebKit code to be ARC-compatible
[WebKit-https.git] / Source / WebKit / Shared / API / Cocoa / WKRemoteObjectCoder.mm
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WKRemoteObjectCoder.h"
28
29 #if WK_API_ENABLED
30
31 #import "APIArray.h"
32 #import "APIData.h"
33 #import "APIDictionary.h"
34 #import "APINumber.h"
35 #import "APIString.h"
36 #import "NSInvocationSPI.h"
37 #import "_WKRemoteObjectInterfaceInternal.h"
38 #import <objc/runtime.h>
39 #import <wtf/RetainPtr.h>
40 #import <wtf/SetForScope.h>
41 #import <wtf/text/CString.h>
42
43 static const char* const classNameKey = "$class";
44 static const char* const objectStreamKey = "$objectStream";
45 static const char* const stringKey = "$string";
46
47 static NSString * const selectorKey = @"selector";
48 static NSString * const typeStringKey = @"typeString";
49 static NSString * const isReplyBlockKey = @"isReplyBlock";
50
51 static RefPtr<API::Dictionary> createEncodedObject(WKRemoteObjectEncoder *, id);
52
53 @interface NSMethodSignature ()
54 - (NSString *)_typeString;
55 @end
56
57 @interface NSCoder ()
58 - (void)validateClassSupportsSecureCoding:(Class)objectClass;
59 @end
60
61 @implementation WKRemoteObjectEncoder {
62     RefPtr<API::Dictionary> _rootDictionary;
63     API::Array* _objectStream;
64
65     API::Dictionary* _currentDictionary;
66 }
67
68 - (id)init
69 {
70     if (!(self = [super init]))
71         return nil;
72
73     _rootDictionary = API::Dictionary::create();
74     _currentDictionary = _rootDictionary.get();
75
76     return self;
77 }
78
79 #if !ASSERT_DISABLED
80 - (void)dealloc
81 {
82     ASSERT(_currentDictionary == _rootDictionary);
83
84     [super dealloc];
85 }
86 #endif
87
88 - (API::Dictionary*)rootObjectDictionary
89 {
90     return _rootDictionary.get();
91 }
92
93 static void ensureObjectStream(WKRemoteObjectEncoder *encoder)
94 {
95     if (encoder->_objectStream)
96         return;
97
98     Ref<API::Array> objectStream = API::Array::create();
99     encoder->_objectStream = objectStream.ptr();
100
101     encoder->_rootDictionary->set(objectStreamKey, WTFMove(objectStream));
102 }
103
104 static void encodeToObjectStream(WKRemoteObjectEncoder *encoder, id value)
105 {
106     ensureObjectStream(encoder);
107
108     size_t position = encoder->_objectStream->size();
109     encoder->_objectStream->elements().append(nullptr);
110
111     auto encodedObject = createEncodedObject(encoder, value);
112     ASSERT(!encoder->_objectStream->elements()[position]);
113     encoder->_objectStream->elements()[position] = WTFMove(encodedObject);
114 }
115
116 static void encodeInvocationArguments(WKRemoteObjectEncoder *encoder, NSInvocation *invocation, NSUInteger firstArgument)
117 {
118     NSMethodSignature *methodSignature = invocation.methodSignature;
119     NSUInteger argumentCount = methodSignature.numberOfArguments;
120
121     ASSERT(firstArgument <= argumentCount);
122
123     for (NSUInteger i = firstArgument; i < argumentCount; ++i) {
124         const char* type = [methodSignature getArgumentTypeAtIndex:i];
125
126         switch (*type) {
127         // double
128         case 'd': {
129             double value;
130             [invocation getArgument:&value atIndex:i];
131
132             encodeToObjectStream(encoder, @(value));
133             break;
134         }
135
136         // float
137         case 'f': {
138             float value;
139             [invocation getArgument:&value atIndex:i];
140
141             encodeToObjectStream(encoder, @(value));
142             break;
143         }
144
145         // int
146         case 'i': {
147             int value;
148             [invocation getArgument:&value atIndex:i];
149
150             encodeToObjectStream(encoder, @(value));
151             break;
152         }
153
154         // unsigned
155         case 'I': {
156             unsigned value;
157             [invocation getArgument:&value atIndex:i];
158
159             encodeToObjectStream(encoder, @(value));
160             break;
161         }
162
163         // char
164         case 'c': {
165             char value;
166             [invocation getArgument:&value atIndex:i];
167
168             encodeToObjectStream(encoder, @(value));
169             break;
170         }
171
172         // bool
173         case 'B': {
174             BOOL value;
175             [invocation getArgument:&value atIndex:i];
176
177             encodeToObjectStream(encoder, @(value));
178             break;
179         }
180
181         // long
182         case 'q': {
183             long value;
184             [invocation getArgument:&value atIndex:i];
185
186             encodeToObjectStream(encoder, @(value));
187             break;
188         }
189
190         // unsigned long
191         case 'Q': {
192             unsigned long value;
193             [invocation getArgument:&value atIndex:i];
194
195             encodeToObjectStream(encoder, @(value));
196             break;
197         }
198
199         // Objective-C object
200         case '@': {
201             id value;
202             [invocation getArgument:&value atIndex:i];
203
204             encodeToObjectStream(encoder, value);
205             break;
206         }
207
208         // struct
209         case '{':
210             if (!strcmp(type, @encode(NSRange))) {
211                 NSRange value;
212                 [invocation getArgument:&value atIndex:i];
213
214                 encodeToObjectStream(encoder, [NSValue valueWithRange:value]);
215                 break;
216             } else if (!strcmp(type, @encode(CGSize))) {
217                 CGSize value;
218                 [invocation getArgument:&value atIndex:i];
219
220                 encodeToObjectStream(encoder, @(value.width));
221                 encodeToObjectStream(encoder, @(value.height));
222                 break;
223             }
224             FALLTHROUGH;
225
226         default:
227             [NSException raise:NSInvalidArgumentException format:@"Unsupported invocation argument type '%s'", type];
228         }
229     }
230 }
231
232 static void encodeInvocation(WKRemoteObjectEncoder *encoder, NSInvocation *invocation)
233 {
234     NSMethodSignature *methodSignature = invocation.methodSignature;
235     [encoder encodeObject:methodSignature._typeString forKey:typeStringKey];
236
237     if ([invocation isKindOfClass:[NSBlockInvocation class]]) {
238         [encoder encodeBool:YES forKey:isReplyBlockKey];
239         encodeInvocationArguments(encoder, invocation, 1);
240     } else {
241         [encoder encodeObject:NSStringFromSelector(invocation.selector) forKey:selectorKey];
242         encodeInvocationArguments(encoder, invocation, 2);
243     }
244 }
245
246 static void encodeString(WKRemoteObjectEncoder *encoder, NSString *string)
247 {
248     encoder->_currentDictionary->set(stringKey, API::String::create(string));
249 }
250
251 static void encodeObject(WKRemoteObjectEncoder *encoder, id object)
252 {
253     ASSERT(object);
254
255     if (![object conformsToProtocol:@protocol(NSSecureCoding)] && ![object isKindOfClass:[NSInvocation class]])
256         [NSException raise:NSInvalidArgumentException format:@"%@ does not conform to NSSecureCoding", object];
257
258     if (class_isMetaClass(object_getClass(object)))
259         [NSException raise:NSInvalidArgumentException format:@"Class objects may not be encoded"];
260
261     Class objectClass = [object classForCoder];
262     if (!objectClass)
263         [NSException raise:NSInvalidArgumentException format:@"-classForCoder returned nil for %@", object];
264
265     encoder->_currentDictionary->set(classNameKey, API::String::create(class_getName(objectClass)));
266
267     if ([object isKindOfClass:[NSInvocation class]]) {
268         // We have to special case NSInvocation since we don't want to encode the target.
269         encodeInvocation(encoder, object);
270         return;
271     }
272
273     if (objectClass == [NSString class] || objectClass == [NSMutableString class]) {
274         encodeString(encoder, object);
275         return;
276     }
277
278     [object encodeWithCoder:encoder];
279 }
280
281 static RefPtr<API::Dictionary> createEncodedObject(WKRemoteObjectEncoder *encoder, id object)
282 {
283     if (!object)
284         return nil;
285
286     Ref<API::Dictionary> dictionary = API::Dictionary::create();
287     SetForScope<API::Dictionary*> dictionaryChange(encoder->_currentDictionary, dictionary.ptr());
288
289     encodeObject(encoder, object);
290
291     return WTFMove(dictionary);
292 }
293
294 - (void)encodeValueOfObjCType:(const char *)type at:(const void *)address
295 {
296     switch (*type) {
297     // int
298     case 'i':
299         encodeToObjectStream(self, @(*static_cast<const int*>(address)));
300         break;
301
302     // Objective-C object.
303     case '@':
304         encodeToObjectStream(self, *static_cast<const id*>(address));
305         break;
306
307     default:
308         [NSException raise:NSInvalidArgumentException format:@"Unsupported type '%s'", type];
309     }
310 }
311
312 - (BOOL)allowsKeyedCoding
313 {
314     return YES;
315 }
316
317 static NSString *escapeKey(NSString *key)
318 {
319     if (key.length && [key characterAtIndex:0] == '$')
320         return [@"$" stringByAppendingString:key];
321
322     return key;
323 }
324
325 - (void)encodeObject:(id)object forKey:(NSString *)key
326 {
327     _currentDictionary->set(escapeKey(key), createEncodedObject(self, object));
328 }
329
330 - (void)encodeBytes:(const uint8_t *)bytes length:(NSUInteger)length forKey:(NSString *)key
331 {
332     _currentDictionary->set(escapeKey(key), API::Data::create(bytes, length));
333 }
334
335 - (void)encodeBool:(BOOL)value forKey:(NSString *)key
336 {
337     _currentDictionary->set(escapeKey(key), API::Boolean::create(value));
338 }
339
340 - (void)encodeInt:(int)value forKey:(NSString *)key
341 {
342     _currentDictionary->set(escapeKey(key), API::UInt64::create(value));
343 }
344
345 - (void)encodeInt32:(int32_t)value forKey:(NSString *)key
346 {
347     _currentDictionary->set(escapeKey(key), API::UInt64::create(value));
348 }
349
350 - (void)encodeInt64:(int64_t)value forKey:(NSString *)key
351 {
352     _currentDictionary->set(escapeKey(key), API::UInt64::create(value));
353 }
354
355 - (void)encodeInteger:(NSInteger)intv forKey:(NSString *)key
356 {
357     return [self encodeInt64:intv forKey:key];
358 }
359
360 - (void)encodeFloat:(float)value forKey:(NSString *)key
361 {
362     _currentDictionary->set(escapeKey(key), API::Double::create(value));
363 }
364
365 - (void)encodeDouble:(double)value forKey:(NSString *)key
366 {
367     _currentDictionary->set(escapeKey(key), API::Double::create(value));
368 }
369
370 - (BOOL)requiresSecureCoding
371 {
372     return YES;
373 }
374
375 @end
376
377 @implementation WKRemoteObjectDecoder {
378     RetainPtr<_WKRemoteObjectInterface> _interface;
379
380     const API::Dictionary* _rootDictionary;
381     const API::Dictionary* _currentDictionary;
382
383     SEL _replyToSelector;
384
385     const API::Array* _objectStream;
386     size_t _objectStreamPosition;
387
388     const HashSet<CFTypeRef>* _allowedClasses;
389 }
390
391 - (id)initWithInterface:(_WKRemoteObjectInterface *)interface rootObjectDictionary:(const API::Dictionary*)rootObjectDictionary replyToSelector:(SEL)replyToSelector
392 {
393     if (!(self = [super init]))
394         return nil;
395
396     _interface = interface;
397
398     _rootDictionary = rootObjectDictionary;
399     _currentDictionary = _rootDictionary;
400
401     _replyToSelector = replyToSelector;
402
403     _objectStream = _rootDictionary->get<API::Array>(objectStreamKey);
404
405     return self;
406 }
407
408 - (void)decodeValueOfObjCType:(const char *)type at:(void *)data
409 {
410     switch (*type) {
411     // int
412     case 'i':
413         *static_cast<int*>(data) = [decodeObjectFromObjectStream(self, { (__bridge CFTypeRef)[NSNumber class] }) intValue];
414         break;
415
416     default:
417         [NSException raise:NSInvalidUnarchiveOperationException format:@"Unsupported type '%s'", type];
418     }
419 }
420
421 - (BOOL)allowsKeyedCoding
422 {
423     return YES;
424 }
425
426 - (BOOL)containsValueForKey:(NSString *)key
427 {
428     return _currentDictionary->map().contains(escapeKey(key));
429 }
430
431 - (id)decodeObjectForKey:(NSString *)key
432 {
433     return [self decodeObjectOfClasses:nil forKey:key];
434 }
435
436 static id decodeObject(WKRemoteObjectDecoder *, const API::Dictionary*, const HashSet<CFTypeRef>& allowedClasses);
437
438 static id decodeObjectFromObjectStream(WKRemoteObjectDecoder *decoder, const HashSet<CFTypeRef>& allowedClasses)
439 {
440     if (!decoder->_objectStream)
441         return nil;
442
443     if (decoder->_objectStreamPosition == decoder->_objectStream->size())
444         return nil;
445
446     const API::Dictionary* dictionary = decoder->_objectStream->at<API::Dictionary>(decoder->_objectStreamPosition++);
447
448     return decodeObject(decoder, dictionary, allowedClasses);
449 }
450
451 static void checkIfClassIsAllowed(WKRemoteObjectDecoder *decoder, Class objectClass)
452 {
453     auto* allowedClasses = decoder->_allowedClasses;
454     if (!allowedClasses)
455         return;
456
457     if (allowedClasses->contains((__bridge CFTypeRef)objectClass))
458         return;
459
460     for (Class superclass = class_getSuperclass(objectClass); superclass; superclass = class_getSuperclass(superclass)) {
461         if (allowedClasses->contains((__bridge CFTypeRef)superclass))
462             return;
463     }
464
465     [NSException raise:NSInvalidUnarchiveOperationException format:@"Object of class \"%@\" is not allowed. Allowed classes are \"%@\"", objectClass, decoder.allowedClasses];
466 }
467
468 static void validateClass(WKRemoteObjectDecoder *decoder, Class objectClass)
469 {
470     ASSERT(objectClass);
471
472     checkIfClassIsAllowed(decoder, objectClass);
473
474     // NSInvocation and NSBlockInvocation don't support NSSecureCoding, but we allow them anyway.
475     if (objectClass == [NSInvocation class] || objectClass == [NSBlockInvocation class])
476         return;
477
478     [decoder validateClassSupportsSecureCoding:objectClass];
479 }
480
481 static void decodeInvocationArguments(WKRemoteObjectDecoder *decoder, NSInvocation *invocation, const Vector<HashSet<CFTypeRef>>& allowedArgumentClasses, NSUInteger firstArgument)
482 {
483     NSMethodSignature *methodSignature = invocation.methodSignature;
484     NSUInteger argumentCount = methodSignature.numberOfArguments;
485
486     ASSERT(firstArgument <= argumentCount);
487
488     for (NSUInteger i = firstArgument; i < argumentCount; ++i) {
489         const char* type = [methodSignature getArgumentTypeAtIndex:i];
490
491         switch (*type) {
492         // double
493         case 'd': {
494             double value = [decodeObjectFromObjectStream(decoder, { (__bridge CFTypeRef)[NSNumber class] }) doubleValue];
495             [invocation setArgument:&value atIndex:i];
496             break;
497         }
498
499         // float
500         case 'f': {
501             float value = [decodeObjectFromObjectStream(decoder, { (__bridge CFTypeRef)[NSNumber class] }) floatValue];
502             [invocation setArgument:&value atIndex:i];
503             break;
504         }
505
506         // int
507         case 'i': {
508             int value = [decodeObjectFromObjectStream(decoder, { (__bridge CFTypeRef)[NSNumber class] }) intValue];
509             [invocation setArgument:&value atIndex:i];
510             break;
511         }
512
513         // unsigned
514         case 'I': {
515             unsigned value = [decodeObjectFromObjectStream(decoder, { (__bridge CFTypeRef)[NSNumber class] }) unsignedIntValue];
516             [invocation setArgument:&value atIndex:i];
517             break;
518         }
519
520         // char
521         case 'c': {
522             char value = [decodeObjectFromObjectStream(decoder, { (__bridge CFTypeRef)[NSNumber class] }) charValue];
523             [invocation setArgument:&value atIndex:i];
524             break;
525         }
526
527         // bool
528         case 'B': {
529             bool value = [decodeObjectFromObjectStream(decoder, { (__bridge CFTypeRef)[NSNumber class] }) boolValue];
530             [invocation setArgument:&value atIndex:i];
531             break;
532         }
533
534         // long
535         case 'q': {
536             long value = [decodeObjectFromObjectStream(decoder, { (__bridge CFTypeRef)[NSNumber class] }) longValue];
537             [invocation setArgument:&value atIndex:i];
538             break;
539         }
540
541         // unsigned long
542         case 'Q': {
543             unsigned long value = [decodeObjectFromObjectStream(decoder, { (__bridge CFTypeRef)[NSNumber class] }) unsignedLongValue];
544             [invocation setArgument:&value atIndex:i];
545             break;
546         }
547             
548         // Objective-C object
549         case '@': {
550             auto& allowedClasses = allowedArgumentClasses[i - firstArgument];
551
552             id value = decodeObjectFromObjectStream(decoder, allowedClasses);
553             [invocation setArgument:&value atIndex:i];
554
555             // FIXME: Make sure the invocation doesn't outlive the value.
556             break;
557         }
558
559         // struct
560         case '{':
561             if (!strcmp(type, @encode(NSRange))) {
562                 NSRange value = [decodeObjectFromObjectStream(decoder, { (__bridge CFTypeRef)[NSValue class] }) rangeValue];
563                 [invocation setArgument:&value atIndex:i];
564                 break;
565             } else if (!strcmp(type, @encode(CGSize))) {
566                 CGSize value;
567                 value.width = [decodeObjectFromObjectStream(decoder, { (__bridge CFTypeRef)[NSNumber class] }) doubleValue];
568                 value.height = [decodeObjectFromObjectStream(decoder, { (__bridge CFTypeRef)[NSNumber class] }) doubleValue];
569                 [invocation setArgument:&value atIndex:i];
570                 break;
571             }
572             FALLTHROUGH;
573
574         default:
575             [NSException raise:NSInvalidArgumentException format:@"Unsupported invocation argument type '%s' for argument %zu", type, (unsigned long)i];
576         }
577     }
578 }
579
580 static NSInvocation *decodeInvocation(WKRemoteObjectDecoder *decoder)
581 {
582     SEL selector = nullptr;
583     NSMethodSignature *localMethodSignature = nil;
584
585     BOOL isReplyBlock = [decoder decodeBoolForKey:isReplyBlockKey];
586
587     if (isReplyBlock) {
588         if (!decoder->_replyToSelector)
589             [NSException raise:NSInvalidUnarchiveOperationException format:@"%@: Received unknown reply block", decoder];
590
591         localMethodSignature = [decoder->_interface _methodSignatureForReplyBlockOfSelector:decoder->_replyToSelector];
592         if (!localMethodSignature)
593             [NSException raise:NSInvalidUnarchiveOperationException format:@"Reply block for selector \"%s\" is not defined in the local interface", sel_getName(decoder->_replyToSelector)];
594     } else {
595         NSString *selectorString = [decoder decodeObjectOfClass:[NSString class] forKey:selectorKey];
596         if (!selectorString)
597             [NSException raise:NSInvalidUnarchiveOperationException format:@"Invocation had no selector"];
598
599         selector = NSSelectorFromString(selectorString);
600         ASSERT(selector);
601
602         localMethodSignature = [decoder->_interface _methodSignatureForSelector:selector];
603         if (!localMethodSignature)
604             [NSException raise:NSInvalidUnarchiveOperationException format:@"Selector \"%@\" is not defined in the local interface", selectorString];
605     }
606
607     NSString *typeSignature = [decoder decodeObjectOfClass:[NSString class] forKey:typeStringKey];
608     if (!typeSignature)
609         [NSException raise:NSInvalidUnarchiveOperationException format:@"Invocation had no type signature"];
610
611     NSMethodSignature *remoteMethodSignature = [NSMethodSignature signatureWithObjCTypes:typeSignature.UTF8String];
612     localMethodSignature = remoteMethodSignature;
613     if (![localMethodSignature isEqual:remoteMethodSignature])
614         [NSException raise:NSInvalidUnarchiveOperationException format:@"Local and remote method signatures are not equal for method \"%s\"", selector ? sel_getName(selector) : "(no selector)"];
615
616     NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:localMethodSignature];
617
618     if (isReplyBlock) {
619         const auto& allowedClasses = [decoder->_interface _allowedArgumentClassesForReplyBlockOfSelector:decoder->_replyToSelector];
620         decodeInvocationArguments(decoder, invocation, allowedClasses, 1);
621     } else {
622         const auto& allowedClasses = [decoder->_interface _allowedArgumentClassesForSelector:selector];
623         decodeInvocationArguments(decoder, invocation, allowedClasses, 2);
624
625         [invocation setArgument:&selector atIndex:1];
626     }
627
628     return invocation;
629 }
630
631 static NSString *decodeString(WKRemoteObjectDecoder *decoder)
632 {
633     API::String* string = decoder->_currentDictionary->get<API::String>(stringKey);
634     if (!string)
635         [NSException raise:NSInvalidUnarchiveOperationException format:@"String missing"];
636
637     return string->string();
638 }
639
640 static id decodeObject(WKRemoteObjectDecoder *decoder)
641 {
642     API::String* classNameString = decoder->_currentDictionary->get<API::String>(classNameKey);
643     if (!classNameString)
644         [NSException raise:NSInvalidUnarchiveOperationException format:@"Class name missing"];
645
646     CString className = classNameString->string().utf8();
647
648     Class objectClass = objc_lookUpClass(className.data());
649     if (!objectClass)
650         [NSException raise:NSInvalidUnarchiveOperationException format:@"Class \"%s\" does not exist", className.data()];
651
652     validateClass(decoder, objectClass);
653
654     if (objectClass == [NSInvocation class] || objectClass == [NSBlockInvocation class])
655         return decodeInvocation(decoder);
656
657     if (objectClass == [NSString class])
658         return decodeString(decoder);
659
660     if (objectClass == [NSMutableString class])
661         return [NSMutableString stringWithString:decodeString(decoder)];
662
663     id result = [objectClass allocWithZone:decoder.zone];
664     if (!result)
665         [NSException raise:NSInvalidUnarchiveOperationException format:@"Class \"%s\" returned nil from +alloc while being decoded", className.data()];
666
667     result = [result initWithCoder:decoder];
668     if (!result)
669         [NSException raise:NSInvalidUnarchiveOperationException format:@"Object of class \"%s\" returned nil from -initWithCoder: while being decoded", className.data()];
670
671     result = [result awakeAfterUsingCoder:decoder];
672     if (!result)
673         [NSException raise:NSInvalidUnarchiveOperationException format:@"Object of class \"%s\" returned nil from -awakeAfterUsingCoder: while being decoded", className.data()];
674
675     return [result autorelease];
676 }
677
678 static id decodeObject(WKRemoteObjectDecoder *decoder, const API::Dictionary* dictionary, const HashSet<CFTypeRef>& allowedClasses)
679 {
680     if (!dictionary)
681         return nil;
682
683     SetForScope<const API::Dictionary*> dictionaryChange(decoder->_currentDictionary, dictionary);
684
685     // If no allowed classes were listed, just use the currently allowed classes.
686     if (allowedClasses.isEmpty())
687         return decodeObject(decoder);
688
689     SetForScope<const HashSet<CFTypeRef>*> allowedClassesChange(decoder->_allowedClasses, &allowedClasses);
690     return decodeObject(decoder);
691 }
692
693 - (BOOL)decodeBoolForKey:(NSString *)key
694 {
695     const API::Boolean* value = _currentDictionary->get<API::Boolean>(escapeKey(key));
696     if (!value)
697         return false;
698     return value->value();
699 }
700
701 - (int)decodeIntForKey:(NSString *)key
702 {
703     const API::UInt64* value = _currentDictionary->get<API::UInt64>(escapeKey(key));
704     if (!value)
705         return 0;
706     return static_cast<int>(value->value());
707 }
708
709 - (int32_t)decodeInt32ForKey:(NSString *)key
710 {
711     const API::UInt64* value = _currentDictionary->get<API::UInt64>(escapeKey(key));
712     if (!value)
713         return 0;
714     return static_cast<int32_t>(value->value());
715 }
716
717 - (int64_t)decodeInt64ForKey:(NSString *)key
718 {
719     const API::UInt64* value = _currentDictionary->get<API::UInt64>(escapeKey(key));
720     if (!value)
721         return 0;
722     return value->value();
723 }
724
725 - (NSInteger)decodeIntegerForKey:(NSString *)key
726 {
727     return [self decodeInt64ForKey:key];
728 }
729
730 - (float)decodeFloatForKey:(NSString *)key
731 {
732     const API::Double* value = _currentDictionary->get<API::Double>(escapeKey(key));
733     if (!value)
734         return 0;
735     return value->value();
736 }
737
738 - (double)decodeDoubleForKey:(NSString *)key
739 {
740     const API::Double* value = _currentDictionary->get<API::Double>(escapeKey(key));
741     if (!value)
742         return 0;
743     return value->value();
744 }
745
746 - (const uint8_t *)decodeBytesForKey:(NSString *)key returnedLength:(NSUInteger *)length
747 {
748     auto* data = _currentDictionary->get<API::Data>(escapeKey(key));
749     if (!data || !data->size()) {
750         *length = 0;
751         return nullptr;
752     }
753
754     *length = data->size();
755     return data->bytes();
756 }
757
758 - (BOOL)requiresSecureCoding
759 {
760     return YES;
761 }
762
763 - (id)decodeObjectOfClasses:(NSSet *)classes forKey:(NSString *)key
764 {
765     HashSet<CFTypeRef> allowedClasses;
766     for (Class allowedClass in classes)
767         allowedClasses.add((__bridge CFTypeRef)allowedClass);
768
769     return decodeObject(self, _currentDictionary->get<API::Dictionary>(escapeKey(key)), allowedClasses);
770 }
771
772 - (NSSet *)allowedClasses
773 {
774     if (!_allowedClasses)
775         return [NSSet set];
776
777     auto result = adoptNS([[NSMutableSet alloc] init]);
778     for (auto allowedClass : *_allowedClasses)
779         [result addObject:(__bridge Class)allowedClass];
780
781     return result.autorelease();
782 }
783
784 @end
785
786 #endif // WK_API_ENABLED