1/*
2 * Copyright (c) 2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20//
21//  Radar5983285.m
22//  Copyright (c) 2008-2011 Apple Inc. All rights reserved.
23//
24
25#import "WhiteBoxTest.h"
26
27@interface Radar5983285 : WhiteBoxTest {
28    BOOL _blockWasPended;
29    BOOL _blockWasScanned;
30    vm_address_t disguisedPointer;
31    BOOL testBlockCollected;
32    const unsigned char *correctLayout;
33    BOOL scannedWithBogusLayout;
34    BOOL scannedWithCorrectLayout;
35}
36
37@property(readwrite, nonatomic) vm_address_t disguisedPointer;
38@property(readwrite, nonatomic) BOOL testBlockCollected;
39@property(readwrite, nonatomic) const unsigned char *correctLayout;
40@property(readwrite, nonatomic) BOOL scannedWithBogusLayout;
41@property(readwrite, nonatomic) BOOL scannedWithCorrectLayout;
42
43@end
44
45// simple test class with nontrivial layout
46@interface Radar5983285_TestClass : NSObject
47{
48    id foo;
49    int bar;
50    id cat;
51}
52@end
53@implementation Radar5983285_TestClass
54@end
55
56@implementation Radar5983285
57
58@synthesize disguisedPointer;
59@synthesize testBlockCollected;
60@synthesize correctLayout;
61@synthesize scannedWithBogusLayout;
62@synthesize scannedWithCorrectLayout;
63
64- (void)allocateTestBlock
65{
66    // allocate a test block and fetch its layout
67    id testObject = [Radar5983285_TestClass new];
68    auto_collection_control_t *control = auto_collection_parameters([self auto_zone]);
69    self.correctLayout = control->layout_for_address([self auto_zone], testObject);
70    [self setDisguisedPointer:[self disguise:testObject]];
71
72    // keep a reference to the test block on the stack
73    [self testThreadStackBuffer][0] = testObject;
74}
75
76
77- (void)testResult
78{
79    // check that our block was scanned with the correct layout
80    if (self.scannedWithBogusLayout)
81        [self fail:@"block was scanned with invalid layout"];
82    else
83        [self passed];
84    [self testFinished];
85}
86
87- (void)performTest
88{
89    [self performSelectorOnTestThread:@selector(allocateTestBlock)];
90
91    // request a generational collection and go to sleep until our block gets pended
92    [self requestGenerationalCollectionWithCompletionCallback:^{ [self testResult]; }];
93}
94
95- (void)blockWasPended
96{
97    if (_blockWasPended) {
98        // The heap collector is about to pend our test block.
99        // Clear our local reference and run a local collection.
100        [self testThreadStackBuffer][0] = nil;
101        [self runThreadLocalCollection];
102
103        if (!self.testBlockCollected)
104            [self fail:@"test block was not collected as expected during local collection"];
105
106        // now wake up the heap collector
107    } else {
108        [self fail:@"test block was not pended as expected"];
109    }
110}
111
112- (void)setPending:(void *)block
113{
114    // wake up test thread once our block gets pended
115    if (block == [self undisguise:self.disguisedPointer]) {
116        _blockWasPended = YES;
117        [self performSelectorOnTestThread:@selector(blockWasPended)];
118    }
119    [super setPending:block];
120}
121
122- (void)scanBlock:(void *)block endAddress:(void *)end
123{
124    // our block has layout info so should not be scanned conservatively
125    if (block == [self undisguise:self.disguisedPointer] && !self.scannedWithCorrectLayout) {
126        [self fail:@"test block scanned without layout"];
127    }
128    [super scanBlock:block endAddress:end];
129}
130
131- (void)scanBlock:(void *)block endAddress:(void *)end withLayout:(const unsigned char *)map
132{
133    // verify the block's layout
134    if (block == [self undisguise:self.disguisedPointer]) {
135        if (map != self.correctLayout)
136            self.scannedWithBogusLayout = YES;
137        else
138            self.scannedWithCorrectLayout = YES;
139    }
140    [super scanBlock:block endAddress:end withLayout:map];
141}
142
143- (void)endLocalScanWithGarbage:(void **)garbage_list count:(size_t)count
144{
145    // monitor for our test block becoming garbage
146    if ([self block:[self undisguise:self.disguisedPointer] isInList:garbage_list count:count]) {
147        self.testBlockCollected = YES;
148    }
149    [super endLocalScanWithGarbage:garbage_list count:count];
150}
151
152- (void)blockBecameGlobal:(void *)block withAge:(uint32_t)age
153{
154    // monitor if our test block becomes global (it should not)
155    if (block == [self undisguise:self.disguisedPointer]) {
156        [self fail:@"test block became global unexpectedly"];
157    }
158    [super blockBecameGlobal:block withAge:age];
159}
160
161@end
162