560 likes | 572 Views
Explore the concept of reflection in Smalltalk, including introspection and the ability to change objects at runtime.
E N D
Orphan Objects and Other Reflective Techniques Brian Foote The Refactory, Inc. 23 April 2001 Smalltalk Solutions ‘02 Orphan Objects
Reflective Programming in Smalltalk Definitions Building a Language Out of Objects Examples Orphan Objects
Frequently Asked Questions Q: What isreflection? A: It’s about building your language out of first-class, dynamic objects that you can look at and change at runtime. Orphan Objects
Frequently Asked Questions Q: Haven’t people really been doing this for a long time? A: They certainly have. Smalltalk programmers have been doing this since the ‘70s. Orphan Objects
Reflection from the Eisenhower Era Hence, [the machine] can, in particular, change the orders (since these are in memory!)--the very orders that control its actions --John Von Neumann 1958 Orphan Objects
Frequently Asked Questions Q: What about efficiency? A: Orphan Objects
Frequently Asked Questions Q: What about efficiency? A: There are a variety of techniques that can be used to make reflective systems faster. Orphan Objects
Frequently Asked Questions Q: Isn’t reflection dangerous? A: Yes! You bet it is! A: Yes, if you are not careful. A: Yes, but you can make it safer. A: Yes, but so is crossing the street. Orphan Objects
Frequently Asked Questions Q: Isn’t reflection just a fancy name for a few clever hacks? A: Well, that and so much more... Orphan Objects
Frequently Asked Questions Q: Why is so much of the reflection literature so hard to read? A: This is due, in part, to the area’s AI heritage... Orphan Objects
Reflective Terminology introspection reflection reification Orphan Objects
Reflective Terminology infinite regress causal connection reflective tower Orphan Objects
Introspection When a program can look at at the objects from which it is built Smalltalk has a rich, comprehensive, indeed, unrivaled collection of introspective facilities Orphan Objects
Reflection When a program can change the objects from which it is built Smalltalk represents as much as it can as as first-class dynamic objects, and you can change them at runtime. Orphan Objects
Consider these Meta considered harmful “Meta” considered harmful Orphan Objects
Neat Hack Hall of Fame Parentless Objects Does Not Understand Metaobjects Lightweight Classes Method Wrappers Byte Code Manipulation Compiled Method Copying Context Manipulation Source Generation Association Hacks Becomes Class Change Instance Variable At Methods On-Demand Orphan Objects
Objects themselves Object Orphan Objects
Object Introspection Object size == basicSize hash identityHash printOn: storeOn: dependents ... allOwners firstOwner nextInstance ownerAfter: instVarAt: isKindOf: class isMemberOf: respondsTo: Orphan Objects
Example: AccessibleObjects Demonstrates: doesNotUnderstand: instVarAt: instVarAt:put: at: at:put: Orphan Objects
Accessible Objects AccessibleObject class methods for: examples example "AccessibleObject example" | temp | temp := AccessibleObject new. temp dog: 'Fido'. temp cat: 'Tabby'. Transcript print: temp dog; cr. Transcript print: temp items; cr. temp keysDo: [:key | Transcript print: key; cr]. Transcript print: (temp variableAt: #items); cr. Transcript endEntry Orphan Objects
Accessible Objects AccessibleObject methods for: accessing at: key "Return the object associated with the given key..." ^self valueAt: key at: key put: value "Store the indicated value at the designated place in our item dictionary... " ^self valueAt: key put: value size "Let's say our size is the size of our item dictionary plus our number of instance variables..." ^self items size + self instVarNames size Orphan Objects
Accessible Objects AccessibleObject methods for: accessing valueAt: key "Return the object associated with the given key..." ^self valueAt: key ifAbsent: [self errorKeyNotFound] valueAt: key put: value "Store the indicated value at the designated place in our item dictionary, unless there is an instance var by that name..." items isNil ifTrue: [items := IdentityDictionary new: 16]. (self hasVariableNamed: key) ifTrue: [^self variableAt: key put: value] ifFalse: [^items at: key put: value] Orphan Objects
Accessible Objects AccessibleObject methods for: instance variable access allInstVarNames "Define a shorthand for this class method..." ^self class allInstVarNames hasVariableNamed: name "Say whether we have a variable by the given name..." ^(self variableIndex: name) ~= 0 instVarNames "Define a shorthand for this class method..." ^self class instVarNames Orphan Objects
Accessible Objects AccessibleObject methods for: instance variable access variableAt: name "Return the named value..." | index | index := self variableIndex: name. index = 0 ifTrue: [self error: 'Bad instance variable name...']. ^self instVarAt: index variableAt: name put: value "Set the named instance variable to the indicated value..." | index | index := self variableIndex: name. index = 0 ifTrue: [self error: 'Bad instance variable name...']. ^self instVarAt: index put: value variableIndex: name "Return the instance variable index for this name, or zero..." ^self class allInstVarNames indexOf: name asString. Orphan Objects
Accessible Objects AccessibleObject methods for: error interception doesNotUnderstand: aMessage "Refer messages we don't understand to our item dictionary..." | selector name args | selector := aMessage selector. name := (selector copyWithout: $:) asSymbol. args := aMessage arguments. (self hasVariableNamed: name) ifTrue: [args size = 0 ifTrue: [^self variableAt: name] ifFalse: [^self variableAt: name put: (args at: 1)]]. (items respondsTo: selector) ifTrue: [^items perform: selector withArguments: args]. args size = 1 ifTrue: [^self valueAt: name put: (args at: 1)] ifFalse: [^self valueAt: name ifAbsent: [^super doesNotUnderstand: aMessage]] Orphan Objects
Classes and Behavior Behavior ClassDescription Class Metaclass Orphan Objects
Organizations ClassBuilder SystemOrganizer ClassOrganizer ClassCategoryReader Orphan Objects
Code Representation MethodDictionary CompiledMethod ByteArray BlockClosure Orphan Objects
Runtime Enviroment Context MethodContext/BlockContext Message MessageSend Orphan Objects
Exceptions and Events Event Exception Signal SignalHandler SignalCollection HandlerList Orphan Objects
Process Scheduling Process ProcessScheduler Semaphore SharedQueue Orphan Objects
Viewing the Program Debugger Decompiler Inspector ChangeList <Browsers> Orphan Objects
Making a Promise Future methodsFor: ‘demonstration’ demo | f | f := Future promising: [2+2]. f printString '4.0' Future class methodsFor: 'instance creation' promising: aBlock | aFuture | aFuture := self new. ^aFuture promising: aBlock Orphan Objects
Creating an Orphan nil subclass: #Future instanceVariableNames: ‘semaphore ' classVariableNames: ‘ ' poolDictionaries: ' ' category: ‘Reflection-Examples’ In VisualWorks, ClassBuilder does the rest. Default implementations of doesNotUnderstand: and class are provided. Orphan Objects
We’ll do it eventually... Future methodsFor: 'initialization/dispatching' promising: aBlock "Create a semaphore, and fork a block that will signal it. The result of this block is stored in result..." semaphore := Semaphore new. [result := aBlock value. semaphore signal] fork. ^self Orphan Objects
Keeping a Promise doesNotUndertand: aMessage "If this is our init message, let it by..." aMessage selector == #promising: ifTrue: [^super perform: aMessage selector withArguments: aMessage arguments]. "Wait until our result is available..." semaphore wait. "If our result is a SmallInteger, it has no oop.." (result isKindOf: SmallInteger) ifTrue: [result := result asFloat]. "Become the result and do the deferred message..." result become: self. ^self perform: aMessage selector withArguments: aMessage arguments Orphan Objects
Metaobjects and Lightweight Classes Orphan Objects
Compiler Classes Compiler Decompiler CodeRegenerator Scanner SmalltalkCompiler Orphan Objects
Compiler Support CodeStream DefineOpcodePool MethodNodeHolder ProgramNodeEnumerator ScannerTable <more> Orphan Objects
Variables and Scopes ArgumentVariable InstanceVariable LocalScope LocalVariable NameScope NullScope PseudoVariable ReceiverVariable RemoteVariable StaticScope StaticVariable TemporaryVariable UndeclaredVariable VariableDefinition Orphan Objects
Parse Tree Nodes ProgramNode MethodNode ParameterNode StatementNode ReturnNode ValueNode ArithmeticLoopNode AssignmentNode CascadeNode ConditionalNode LeafNode BlockNode LiteralNode VariableNode LoopNode SequenceNode SimpleMessageNode MessageNode Orphan Objects
Smalltalk SystemDictionary Association/VariableBinding <class and pool variables> Orphan Objects
Storage and Garbage ObjectMemory MemoryPolicy WeakArray WeakDictionary Orphan Objects
Levels of Representation Source Compiler Parse Node ProgramNode ProgramNode Byte Code VM Decompiler Native Code Orphan Objects
Ways to Wrap Source Code Modifications Byte Code Modifications New Selectors Dispatching Wrappers Class Wrappers Instance Wrappers Method Wrappers Orphan Objects
Compiled Methods Orphan Objects
Method Wrappers Orphan Objects
Method Wrappers valueWithReceiver: anObject arguments: args self beforeMethod. ^[clientMethod valueWithReceiver: anObject arguments: args] valueNowOrOnUnwindDo: [self afterMethod] originalMethodName: argument ^#() receiver: self value: argument Orphan Objects
Multimethods OptimizingVisitor>>visitWithNode: aNode <ParseNode> ^self value optimized OptimizingVisitor>> visitWithNode: aNode <VariableNode> ^aNode lookupIn: self symbolTable Orphan Objects
Parse Tree Orphan Objects