Skip to content

Instantly share code, notes, and snippets.

@iamleeg
Created March 4, 2019 19:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iamleeg/aabdbec6afaae218c8f4ce93a56cf192 to your computer and use it in GitHub Desktop.
Save iamleeg/aabdbec6afaae218c8f4ce93a56cf192 to your computer and use it in GitHub Desktop.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ContractEnforcer<T> : NSProxy
- (T)initWithTarget:(T)target;
+ (T)enforcerWithTarget:(T)target;
@end
NS_ASSUME_NONNULL_END
#import "ContractEnforcer.h"
@interface NSObject (Contract)
- (BOOL)invariant;
@end
@implementation ContractEnforcer
{
id _receiver;
}
- (id)initWithTarget:(id)target
{
_receiver = target;
if ([_receiver respondsToSelector:@selector(invariant)]) {
NSAssert([_receiver invariant], @"Expect the invariant to initially hold");
}
return self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return [_receiver methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
// check precondition
NSString *methodName = NSStringFromSelector([invocation selector]);
NSString *preMethodName = [NSString stringWithFormat:@"pre_%@", methodName];
SEL preMethod = NSSelectorFromString(preMethodName);
if ([_receiver respondsToSelector:preMethod]) {
NSMethodSignature *preSignature = [_receiver methodSignatureForSelector:preMethod];
NSInvocation *invokePrecondition = [NSInvocation invocationWithMethodSignature:preSignature];
for (int i = 2; i < [preSignature numberOfArguments]; i++) {
void *argument = NULL;
[invocation getArgument:&argument atIndex:i];
[invokePrecondition setArgument:&argument atIndex:i];
}
[invokePrecondition setSelector:preMethod];
[invokePrecondition invokeWithTarget:_receiver];
BOOL satisfied = NO;
[invokePrecondition getReturnValue:&satisfied];
NSAssert(satisfied, @"precondition failure invoking [%@ %@]", _receiver, methodName);
}
// now do the method
[invocation invokeWithTarget:_receiver];
// check the postcondition
// check the invariant
}
+ enforcerWithTarget:target
{
return [[self alloc] initWithTarget:target];
}
@end
import Foundation
// assume the existence of a bridging header
@objcMembers
class Foo : NSObject
{
func doAThing() {}
func pre_doAThing() -> Bool {
return false;
}
}
let foo : Foo = Foo()
let enforcingFoo:Foo = ContractEnforcer<Foo>.enforcer(withTarget: foo) // this typechecks!
enforcingFoo.doAThing() // Thread 1: EXC_BAD_ACCESS (code=1, address = 0x300000003)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment