'From Squeak3.2gamma of 15 January 2002 [latest update: #4743] on 5 March 2002 at 9:50:41 pm'! Object subclass: #ObjcBridge instanceVariableNames: 'registry classes id2proxy ' classVariableNames: '' poolDictionaries: '' category: 'Objective-C-Bridge'! !ObjcBridge commentStamp: '' prior: 0! I represent a gateway to Objective-C objects running in the same address space. Features: - sending messages to Objective-C objects known by their address - encoding arguments (as property-list) and decoding returns (specially coded strings) - creation and management of proxies (including GC/cleanup via a weak registry) - looking up Objective-C classes by name Issues: - no call-ins / reverse proxies - NSPoints / NSRects aren't currently encoded but should be - more direct encoding of arguments (via Array of property-list-like Squeak Objects) - dealing with image save / reload ! Object subclass: #ObjcProxy instanceVariableNames: 'bridge id ' classVariableNames: '' poolDictionaries: '' category: 'Objective-C-Bridge'! !ObjcProxy commentStamp: '' prior: 0! I am a proxy for a single Objective-C object. I store its address and the Objective-C bridge that handles the interaction with Objective-C. ! !Object methodsFor: 'printing' stamp: 'MPW 1/1/1901 00:30'! propertyList "Answer a String whose characters are a property-list description of the receiver." ^ PropertyListEncoder process:self. ! ! !Object methodsFor: 'printing' stamp: 'MPW 1/1/1901 00:32'! propertyListOn: aStream ^self printOnStream:aStream. ! ! !Association methodsFor: 'printing' stamp: 'MPW 1/4/1901 08:31'! propertyListOn: aStream aStream write:key; print:'='; write:value. ! ! !Integer methodsFor: 'converting' stamp: 'mpw 8/8/1930 08:22'! objcToSqueak:bridge ^self.! ! !ObjcBridge methodsFor: 'translation' stamp: 'LMS 5/29/2001 17:41'! translateArgs:args ^args propertyList. ! ! !ObjcBridge methodsFor: 'translation' stamp: 'mpw 8/8/1930 07:34'! translateResult:result withSignature:signature ^result objcToSqueak:self. ! ! !ObjcBridge methodsFor: 'translation' stamp: 'LMS 5/18/2001 23:06'! translateStringResult:stringResult withSignature:signature | result | result := signature caseOf:{ ['*'] -> [stringResult]. ['N'] -> [stringResult asNumber]. ['@'] -> [self proxyWithId:stringResult asNumber]. ['R'] -> [Compiler evaluate:stringResult] }. ^result. ! ! !ObjcBridge methodsFor: 'testing' stamp: 'LMS 5/8/2001 21:55'! printString:aString "Testing: 'ObjcBridge new printString:'This is a test'.' " ! ! !ObjcBridge methodsFor: 'primitives' stamp: 'mpw 2/23/2002 12:53'! primConvertData:someProxy "forward the selected message to the Objective-C object. It seems to be easier to access vars from the stack than it is accessing instance vars, so proxy must be on stack. " ! ! !ObjcBridge methodsFor: 'primitives' stamp: 'LMS 5/8/2001 17:01'! primGetClass:objcClassName "Testing: ObjcBridge new getClass:'NSString' " ! ! !ObjcBridge methodsFor: 'primitives' stamp: 'LMS 5/8/2001 17:01'! primSendTo:someProxy messageNamed:msgName "forward the selected message to the Objective-C object. It seems to be easier to access vars from the stack than it is accessing instance vars, so proxy must be on stack. " ! ! !ObjcBridge methodsFor: 'primitives' stamp: 'LMS 5/8/2001 17:01'! primSendTo:someProxy messageNamed:msgName with:arg "forward the selected message to the Objective-C object. It seems to be easier to access vars from the stack than it is accessing instance vars, so proxy must be on stack. " ! ! !ObjcBridge methodsFor: 'forwarding' stamp: 'LMS 5/21/2001 20:49'! forward:aMessage to:anId | signature result args | signature _ self getSignatureForMessage:aMessage selector sentTo:anId. signature ifNotNil:[ args _ self translateArgs:aMessage arguments. result _ self sendTo:anId messageNamed:aMessage selector with:args. result _ self translateResult:result withSignature:signature. ] ifNil: [ self error:'Objc object ', anId printString, ' does not understand ', aMessage selector. result _ nil. ]. ^result. ! ! !ObjcBridge methodsFor: 'forwarding' stamp: 'mpw 8/9/1930 06:42'! getClass:objcClassName "Testing: ObjCBridge new getClass:'NSString' " | classProxy | ^classes at:objcClassName ifAbsent:[ classProxy _ self proxyWithId:(self primGetClass:objcClassName). classes at:objcClassName put:classProxy. ^classProxy. ].! ! !ObjcBridge methodsFor: 'forwarding' stamp: 'mpw 8/8/1930 08:01'! getSignatureForMessage:aMessage sentTo:anId ^true. ! ! !ObjcBridge methodsFor: 'forwarding' stamp: 'mpw 8/8/1930 08:01'! sendTo:someProxy messageNamed:msgName ^self primSendTo:someProxy messageNamed:msgName ! ! !ObjcBridge methodsFor: 'forwarding' stamp: 'mpw 8/8/1930 08:02'! sendTo:someProxy messageNamed:msgName with:arg ^ self primSendTo:someProxy messageNamed:msgName with:arg ! ! !ObjcBridge methodsFor: 'proxy management' stamp: 'mpw 8/9/1930 06:53'! createProxyForId:anId | proxy | proxy _ ObjcProxy bridge:self id:anId. id2proxy at:anId put:proxy. registry add:proxy. " self retainObjcObject:anId. " ^proxy. ! ! !ObjcBridge methodsFor: 'proxy management' stamp: 'mpw 8/8/1930 19:07'! forcePrune Smalltalk garbageCollect. self prune. ! ! !ObjcBridge methodsFor: 'proxy management' stamp: 'LMS 5/21/2001 20:48'! proxyWithId:anId "(id2proxy includesKey: anId) ifTrue: [ Transcript show: 'Already hold '; show: anId; show: (id2proxy at:anId); show: ' within: '; show: id2proxy; cr ]." ^id2proxy at:anId ifAbsent:[self createProxyForId:anId]. ! ! !ObjcBridge methodsFor: 'proxy management' stamp: 'mpw 8/8/1930 19:04'! prune registry finalizeValues. ! ! !ObjcBridge methodsFor: 'proxy management' stamp: 'mpw 8/8/1930 07:58'! releaseObjcObject:anId ^self sendTo:anId messageNamed:#release. ! ! !ObjcBridge methodsFor: 'proxy management' stamp: 'LMS 5/21/2001 17:56'! releaseProxy:aProxy | anId | anId _ aProxy id. self removeProxyForId: anId. ^self releaseObjcObject: anId ! ! !ObjcBridge methodsFor: 'proxy management' stamp: 'LMS 5/21/2001 18:22'! removeProxyForId: anId "Used to remove the anId out of the table, rather than letting it be set to nil." ^id2proxy removeKey: anId ifAbsent: nil! ! !ObjcBridge methodsFor: 'proxy management' stamp: 'mpw 8/8/1930 07:58'! retainObjcObject:anId ^self sendTo:anId messageNamed:#retain. ! ! !ObjcBridge methodsFor: 'initialization' stamp: 'mpw 8/8/1930 08:34'! initialize registry _ WeakRegistry new. id2proxy _ WeakValueDictionary new. classes _ Dictionary new. ! ! !ObjcBridge methodsFor: 'initialization' stamp: 'LMS 5/21/2001 17:53'! shutDown: quitting "LMS poor effort at shut down releasing of proxies" Transcript show: 'Shutting down ObjCbridge instance'; cr. super shutDown: quitting! ! !ObjcBridge class methodsFor: 'testing' stamp: 'mpw 3/5/2002 14:20'! loadBundle:bundlePath " load a bundle or framework " | bridge nsbundle bundle | bridge _ self new initialize. nsbundle _ bridge getClass:'NSBundle'. bundle _ nsbundle bundleWithPath:bundlePath. bundle load. ! ! !ObjcBridge class methodsFor: 'testing' stamp: 'mpw 3/5/2002 21:35'! pdfAsMorph:pdfPath " load a PDF into the an ImageMorph using AppKit via JPEG, better would be direct image transfer NOTE: this assumes you have a category on NSImage implementing 'thumbnailJPEG'" | bridge nsimage pdfImage jpeg imageMorph | bridge _ self new initialize. nsimage _ bridge getClass:'NSImage'. pdfImage _ (nsimage alloc initWithContentsOfFile:pdfPath) autorelease. jpeg _ pdfImage thumbnailJPEG. imageMorph _ ImageMorph new. imageMorph readFromData:(jpeg convertNSData asByteArray). imageMorph openInWorld. ! ! !ObjcBridge class methodsFor: 'initialize/release' stamp: 'mpw 8/8/1930 08:27'! new ^self basicNew initialize. ! ! !ObjcBridge class methodsFor: 'initialize/release' stamp: 'LMS 5/16/2001 19:05'! shutDown: quitting "comment stating purpose of message" Transcript show: 'Shutting down ObjCbridge class'; cr. super shutDown: quitting! ! !ObjcProxy methodsFor: 'initialization' stamp: 'mpw 8/8/1930 07:07'! bridge:anObjcBridge id:integerAddress bridge _ anObjcBridge. id _ integerAddress. ! ! !ObjcProxy methodsFor: 'accessing' stamp: 'mpw 8/8/1930 07:05'! bridge ^bridge ! ! !ObjcProxy methodsFor: 'accessing' stamp: 'mpw 8/8/1930 07:05'! id ^id ! ! !ObjcProxy methodsFor: 'accessing' stamp: 'LMS 5/29/2001 17:42'! propertyListOn: aStream "Generate the property list on the id, not the proxy object itself. These will be converted to integers in the property list and correctly used within the interpreter." ^self id printOnStream:aStream.! ! !ObjcProxy methodsFor: 'finalization' stamp: 'LMS 5/21/2001 20:47'! finalize "Transcript show:'finalize ObjcProxy with id ',id printString; cr." ^bridge releaseProxy:self. ! ! !ObjcProxy methodsFor: 'forwarding' stamp: 'mpw 2/23/2002 12:53'! convertNSData ^bridge primConvertData:id. ! ! !ObjcProxy methodsFor: 'forwarding' stamp: 'mpw 8/8/1930 07:11'! doesNotUnderstand:aMessage ^bridge forward:aMessage to:id. ! ! !ObjcProxy methodsFor: 'forwarding' stamp: 'mpw 8/8/1930 10:22'! release bridge releaseObjcObject:id. ! ! !ObjcProxy class methodsFor: 'as yet unclassified' stamp: 'mpw 8/8/1930 07:07'! bridge:anObjcBridge id:integerAddress ^self new bridge:anObjcBridge id:integerAddress. ! ! !ObjcProxy class methodsFor: 'as yet unclassified' stamp: 'LMS 5/16/2001 19:05'! shutDown: quitting "comment stating purpose of message" Transcript show: 'Shutting down ObjCProxy class'; cr. super shutDown: quitting! ! !Rectangle methodsFor: 'printing' stamp: 'mpw 8/9/1930 07:51'! propertyListOn: aStream " {x=a; y=b; width=c; height=d} " aStream print:'{ x='; write:origin x; print:'; y='; write:origin y; print:'; width='; write:self extent x; print:'; height='; write:self extent y; print:';} '. ! ! !SequenceableCollection methodsFor: 'converting' stamp: 'MPW 1/1/1901 00:10'! leftBrace ^'('.! ! !SequenceableCollection methodsFor: 'converting' stamp: 'MPW 1/1/1901 00:08'! propListSeparator ^$,.! ! !SequenceableCollection methodsFor: 'converting' stamp: 'MPW 1/1/1901 00:11'! rightBrace ^$).! ! !String methodsFor: 'converting' stamp: 'mpw 8/8/1930 09:00'! objcToSqueak:bridge ^bridge translateStringResult:(self copyFrom: 2 to: self size) withSignature:(self copyFrom:1 to:1)! ! !String methodsFor: 'printing' stamp: 'MPW 1/1/1901 00:34'! propertyListOn: aStream "Print inside string quotes, quoting." | x | aStream print: $". 1 to: self size do: [:i | x _ self at: i. x == $' ifTrue: [aStream print: $\]. aStream print: x.]. aStream print: $"! ! !ObjcProxy reorganize! ('initialization' bridge:id:) ('accessing' bridge id propertyListOn:) ('finalization' finalize) ('forwarding' convertNSData doesNotUnderstand: release) ! !ObjcBridge class reorganize! ('testing' loadBundle: pdfAsMorph:) ('initialize/release' new shutDown:) ! !ObjcBridge reorganize! ('translation' translateArgs: translateResult:withSignature: translateStringResult:withSignature:) ('testing' printString:) ('primitives' primConvertData: primGetClass: primSendTo:messageNamed: primSendTo:messageNamed:with:) ('forwarding' forward:to: getClass: getSignatureForMessage:sentTo: sendTo:messageNamed: sendTo:messageNamed:with:) ('proxy management' createProxyForId: forcePrune proxyWithId: prune releaseObjcObject: releaseProxy: removeProxyForId: retainObjcObject:) ('initialization' initialize shutDown:) !