290 likes | 319 Views
Learn how to build a Smalltalk compiler that compiles directly to .NET executables and libraries without the need for a virtual machine. Complete source code available.
E N D
Smalltalk in a .NET World How to write a Smalltalk compiler without writing a VM John Brant brant@refactory.com
#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
Dynamic Typing • Root super type • Understands every message • Sends #doesNotUnderstand: printString ^self doesNotUnderstand: (Message selector: #printString arguments: #()) printString | stream | stream := WriteStream with: String new. self printOn: stream. ^stream contents
method: aBoolean | temp one | aBoolean ifTrue: [temp := 5]. one := 1. ^temp + one temp := nil. 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
2r00001011 2r00000010 SmallIntegers • No support for tagged integers • Real integer objects • 10x slower than tagged integers VW Tagged #Smalltalk
Blocks • Create class for each block • Create object from block class at runtime method ^#(1) collect: (Method-Block new) MonadicBlock subclass: Method-Block instanceVariableNames: ‘’ … value: each ^each + 1 method ^#(1) collect: [:each | each + 1]
method arg block block temp arg temp temp value Block Variables • Variable references • Copied values (method/block arguments) • Active variables (temporaries) arg method: arg | block temp | block := [temp + arg]. temp := 5. ^block value 5
method1 self method2: [^1] method2: aBlock #(2) do: [:each | aBlock value. ^each] method1[ ] method2[ ] do: method2 method1 Block Returns • Simulate using .NET exceptions • 100x slower • Tag object (integer)
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 • ``@node ifTrue: ``@block Compiler primitive: [:codeGen :block | …] evaluate: ``@node uses: ``@block • 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
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 • http://www.refactory.com/Software/SharpSmalltalk/ • VisualWorks .NET DLL • http://www.rileywhite.com/software/assemblyconnect.html • .NET • http://www.microsoft.com/net/ • http://www.go-mono.com/