Smalltalk in a.NET World How to write a Smalltalk compiler without writing a VM John Brant
#Smalltalk Open source Smalltalk compiler Complete Source Available Self hosting Compiles Smalltalk directly to.NET executables and libraries
Building Smalltalk Virtual Machine .NET Virtual Machine Compiler RB parser Class Library ANSI Standard Development tools (debuggers, browsers, etc.)
What’s.NET Anyway? Common class library (collections, GUI, sockets, database, etc.) Common virtual machine and byte code instruction set Language isn’t supposed to matter
Why.NET? (business reasons) Backed by Microsoft Lots of existing code Allow Smalltalk to be used for applications that “require.NET”
Why.NET? (personal reasons) Build a Smalltalk compiler Make an experimental platform Learn.NET Unemployed
Smalltalk vs..NET Smalltalk.NET Strong, dynamic typingStrong, static typing Tagged integersPrimitive integer types BlocksDelegates Resumable ExceptionsNon-resumable exceptions become:, change classes?
Smalltalk vs..NET II Smalltalk.NET All methods are virtualStatic, virtual, & non-virtual methods Add/remove classes & methods Add classes/methods (can’t remove or recompile) ?Structs & Enums
Dynamic Typing Root super type Understands every message Sends #doesNotUnderstand: Root ObjectProxy printString ^self doesNotUnderstand: (Message selector: #printString arguments: #()) printString | stream | stream := WriteStream with: String new. self printOn: stream. ^stream contents
null vs. nil Special object for nil Class constructor initializes variables to nil Method temporaries initialized to nil only if they could be read before written method: aBoolean | temp one | aBoolean ifTrue: [temp := 5]. one := 1. ^temp + one temp := nil.
SmallIntegers No support for tagged integers Real integer objects 10x slower than tagged integers 2r r VW Tagged#Smalltalk
Blocks Create class for each block Create object from block class at runtime method ^#(1) collect: [:each | each + 1] method ^#(1) collect: (Method-Block new) MonadicBlock subclass: Method-Block instanceVariableNames: ‘’ … value: each ^each + 1
Block Variables Variable references Copied values (method/block arguments) Active variables (temporaries) method: arg | block temp | block := [temp + arg]. temp := 5. ^block value method arg block temp block arg temp value arg 5
Block Returns Simulate using.NET exceptions 100x slower Tag object (integer) method1 self method2: [^1] method2: aBlock #(2) do: [:each | aBlock value. ^each] method2 method1 do: method2[ ] method1[ ]
Primitives Some actions aren’t representable (e.g., identityHash, +, etc.) Primitive tag Only one per method Compiler primitive: [] Used anywhere in method User can add new primitives Block code evaluated at compile time
Primitive Example identityHash ^Compiler primitive: [:codeGenerator | codeGenerator call: (System.Object getMethod: 'GetHashCode'); constructNewObject: codeGenerator smalltalk smallIntegerClass initializedConstructor] evaluate: self
Optimized Messages Certain messages aren’t sent (e.g., ifTrue:, whileTrue:, to:do:) Can hard code in compiler Instead use macros and “Compiler primitive: []” syntax
Macros RB’s rewrite rules ifTrue: Compiler primitive: [:codeGen :block | …] evaluate: uses: Allows arbitrary optimizations (e.g., ifNil: isNil ifTrue:) Copy source interval for debugger
Connecting with.NET Over 2500 classes provided Seamless integration | algorithm stream | algorithm := HashAlgorithm create: ‘MD5’. stream := FileStream read: ‘rb.im’. [algorithm computeHash: stream] ensure: [stream close]
Connecting Smalltalk to.NET Class references (Convert or System.Convert) Typed Variables (System.Int32) Generic ObjectWrapper Messages for field, property, and method access Constructors – #new* messages
Method arguments First keyword for method name/rest can be anything Arguments converted to.NET objects is-a tests for arguments Overloaded methods left-to-right, specific-to-generic System.Console::Write(int/char/…/object)
Console Example System.Console write: anObject (anObject isKindOf: SmallInteger) ifTrue: [System.Console write: anObject integer] ifFalse: [(anObject isKindOf: Character) ifTrue: [System.Console write: anObject character] ifFalse: [(anObject isKindOf: String) ifTrue: [System.Console write: anObject string] ifFalse: [System.Console write: anObject]]]
Connecting.NET to Smalltalk Events use add_EventName: / remove_EventName: methods Delegates (#asDelegate:) Button new add_Click: ([:obj :args | …] asDelegate: EventHandler) Exported Properties/Methods (specified by annotations) Interfaces & subclasses (to do)
#Smalltalk Development (Version 1) Proof of concept VisualWorks program Compiled generated text file with ilasm (.NET’s assembler) Not seamless
#Smalltalk Development (Version 2) Read.SIF files VisualWorks DLL connection to.NET Override qualified name lookup in VW to use.NET DLL System.Int32 Parse: ‘1234’ System.Reflection.Emit generate.exe file directly
#Smalltalk Development (Version 3) Code based on Version 2 Self hosting Complete.exe compiler Compile three times (fixed point) Old code compiling new code New code compiling new code Verify new code compiled new code valid
Benchmarks Dolphin#SmalltalkVAVWSqueak add integers add floats access strings create objects copy objects perform selectors evaluate blocks generating fractonaccis generating primes * generating strings forming sets sorting strings
Future work IDE (GUI builders, Browsers, etc.) Add Subclassing/Interfaces Optimizations Reusing intermediate numerical values (e.g., i + j + k) Type inferencing – eliminating SmallIntegers (e.g., 1 to: 10 do: [:i | ]) Write programs
Contact Information #Smalltalk VisualWorks.NET DLL