On 01/06/2011 10:30 AM, Denis Washington wrote:
Hi,
First of all, I'd like to introduce myself. My name is Denis Washington,
and I am a student living in Berlin, Germany. I just stumbled upon GNU
Smalltalk a few months ago, and I must say I love it! I am really
impressed by the beauty of Smalltalk, especially when being able to use
it in the "for-those-who-can-type" style that GNU Smalltalk supports.
Many thanks to Paolo and everyone else who made this happen!
I am currently playing with the idea of implementing GObject
Introspection [1] bindings for GNU Smalltalk, which would allow the
run-time generation of bindings for GObject libraries with the
appropiate introspection metadata. This does not only include GTK+ and
Glib, but many others such as Clutter and GStreamer, for which we would
then have bindings "for free". I don't know if I have the skills needed
to do this (I know C fairly well, but am only learning Smalltalk), but I
guess it doesn't hurt to try. ;)
Anyway, here is my actual question. I want to implement the bindings
generation lazily - a GObject class should be bound only if and when it
is first needed. I looked through the base classes and found out that
there is an Autoload class which does something similar, but only does
autoloading through file-ins. Therefor, I propose the addition of an
AbstractAutoload as a superclass of Autoload where most functionality of
Autoload is moved, plus the ability to let subclasses specify how a
class is autoloaded. For instance, it could have a class method
class:in:loadDo: that allows to create subclasses like the following:
AbstractAutoload subclass: GObjectAutoload [
class: nameSymbol in: aNamespace [
^super
class: nameSymbol
in: aNamespace
load: [
"Create wrapper for GObject class and return it"
];
yourself
]
]
Such an AbstractAutoload would probably also have other uses, for
instance for other automatic bindings or similar magic.
What do you think?
With the attached any object that implements #autoload can be used as
a loader. This ensures that the black magic stays confined in
Autoload/AutoloadClass. For example:
st> Eval [ Autoload class: #Complex in: Smalltalk loader: (PackageLoader
packageAt: 'Complex'). nil ]
"Global garbage collection... done"
nil
st> Complex real: 1 imaginary: 2
Loading package Complex
(1+2i)
(Complex is not a great example since the constructor method #i is not present,
but you get the idea).
Paolo
diff --git a/kernel/Autoload.st b/kernel/Autoload.st
index 4097620..20e6a6c 100644
--- a/kernel/Autoload.st
+++ b/kernel/Autoload.st
@@ -32,10 +32,27 @@
+Kernel.PackageInfo extend [
+ autoload [
+ <category: 'private-autoloading'>
+
+ self fileIn
+ ]
+]
+
+FilePath extend [
+ autoload [
+ <category: 'private-autoloading'>
+
+ self withReadStreamDo: [:rs | rs fileIn ]
+ ]
+]
+
Namespace current: Kernel [
nil subclass: AutoloadClass [
- | superClass methodDictionary instanceSpec subClasses instanceVariables
environment name fileName |
+ "Warning: instance variable indices appear below in #class:in:from:"
+ | superClass methodDictionary instanceSpec subClasses instanceVariables
environment name loader |
<comment: 'I represent the metaclass of an autoloaded class before it is
autoloaded.
Having a proxy for the metaclass as well allows one to send messages to
@@ -43,6 +60,33 @@ the metaclass (such as #methodsFor: to extend it with
class-side methods)
and have the class autoloaded.'>
<category: 'Examples-Useful tools'>
+ AutoloadClass class >> class: nameSymbol in: aNamespace loader: aLoader [
+ | autoload behavior newClass |
+ "Create the metaclass and its sole instance"
+ behavior := Behavior new superclass: Autoload.
+
+ "Turn the metaclass into an instance of AutoloadClass. To do
+ this we create a `prototype' in the form of an array..."
+ newClass := Array new: Kernel.AutoloadClass allInstVarNames size.
+ 1 to: behavior class instSize
+ do: [:i | newClass at: i put: (behavior instVarAt: i)].
+
+ newClass
+ at: 6 put: aNamespace;
+ at: 7 put: nameSymbol;
+ at: 8 put: aLoader.
+
+ "... and change its class magically after it is initialized."
+ newClass changeClassTo: Kernel.AutoloadClass.
+
+ "Now create the instance. We go through some hops because of
+ the very limited set of messages that these classes know
+ about."
+ autoload := behavior new.
+ behavior become: newClass.
+ ^autoload
+ ]
+
name [
"Answer the name of the class to be autoloaded"
@@ -50,13 +94,6 @@ and have the class autoloaded.'>
^name
]
- name: aSymbol [
- "Set to aSymbol the name of the class to be autoloaded"
-
- <category: 'accessing'>
- name := aSymbol
- ]
-
environment [
"Answer the namespace in which the class will be autoloaded"
@@ -64,59 +101,37 @@ and have the class autoloaded.'>
^environment
]
- environment: aNamespace [
- "Set to aNamespace the namespace in which the class will be autoloaded"
-
- <category: 'accessing'>
- environment := aNamespace
- ]
-
- fileName [
- "Answer the name of the file from which the class will be autoloaded"
-
- <category: 'accessing'>
- ^fileName
- ]
-
- fileName: aString [
- "Set to aString the name of the file from which the class will be
autoloaded"
-
- <category: 'accessing'>
- fileName := aString
- ]
-
doesNotUnderstand: aMessage [
"Load the class and resend the message to its metaclass."
<category: 'accessing'>
- ^aMessage reinvokeFor: self loadedMetaclass
+ ^aMessage reinvokeFor: self loadedMetaclass_
]
- loadedMetaclass [
+ loadedMetaclass_ [
"File-in the file and answer the metaclass for the new value of the
association which held the receiver"
<category: 'accessing'>
- ^self loadedClass class
+ ^self loadedClass_ class
]
- loadedClass [
+ loadedClass_ [
"File-in the file and answer the new value of the association which
held the receiver"
<category: 'accessing'>
- | class file |
- self fileName isNil
+ | class saveLoader |
+ loader isNil
ifFalse:
- [file := self fileName.
- self fileName: nil.
- self environment at: self name put: nil.
- FileStream fileIn: file].
- class := self environment at: self name ifAbsent: [nil].
+ [saveLoader := loader.
+ loader := nil.
+ environment at: name put: nil.
+ saveLoader autoload].
+ class := environment at: name ifAbsent: [nil].
class isNil
ifTrue:
- [^Autoload error: 'Autoloaded file should have defined class "'
, name
- , '" but didn''t'].
+ [^Autoload error: '%1 should have defined class %2.%3 but
didn''t' % {saveLoader. environment. name asString}].
^class
]
]
@@ -152,32 +167,24 @@ as #methodsFor: to extend it with class-side methods).'>
<category: 'instance creation'>
"Check if the file exists."
- | autoload behavior newClass |
- (FileDescriptor open: fileNameString mode: FileStream read) close.
-
- "Create the metaclass and its sole instance"
- behavior := Behavior new superclass: Autoload.
+ | autoload file |
+ file := fileNameString asFile.
+ file withReadStreamDo: [ :rs | ].
"Turn the metaclass into an instance of AutoloadClass. To do
this we create a `prototype' in the form of an array and then..."
- newClass := Array new: Kernel.AutoloadClass allInstVarNames size.
- 1 to: behavior class instSize
- do: [:i | newClass at: i put: (behavior instVarAt: i)].
+ ^self class: nameSymbol in: aNamespace loader: file
+ ]
- "... change its class magically."
- newClass changeClassTo: Kernel.AutoloadClass.
+ Autoload class >> class: nameSymbol in: aNamespace loader: aLoader [
+ "Make Smalltalk automatically load the class named nameSymbol
+ and residing in aNamespace from fileNameString when needed"
- "We can now initialize it."
- newClass
- name: nameSymbol;
- fileName: fileNameString;
- environment: aNamespace.
+ <category: 'instance creation'>
+ "Check if the file exists."
- "Now create the instance. We go through some hops because of
- the very limited set of messages that these classes know
- about."
- autoload := behavior new.
- behavior become: newClass.
+ | autoload file |
+ autoload := Kernel.AutoloadClass class: nameSymbol in: aNamespace
loader: aLoader.
^aNamespace at: nameSymbol put: autoload
]
@@ -194,7 +201,7 @@ as #methodsFor: to extend it with class-side methods).'>
"Load the class and resend the message to it"
<category: 'accessing'>
- ^aMessage reinvokeFor: self class loadedClass
+ ^aMessage reinvokeFor: self class loadedClass_
]
]
diff --git a/libgst/files.c b/libgst/files.c
index 4b58bbc..535bba2 100644
--- a/libgst/files.c
+++ b/libgst/files.c
@@ -282,14 +282,14 @@ static const char standard_files[] = {
/* Goodies */
"DynVariable.st\0"
- "Autoload.st\0"
"DLD.st\0"
"Getopt.st\0"
"Generator.st\0"
"StreamOps.st\0"
"ObjDumper.st\0"
- "PkgLoader.st\0"
"Regex.st\0"
+ "PkgLoader.st\0"
+ "Autoload.st\0"
};
/* The argc and argv that are passed to libgst via gst_smalltalk_args.
_______________________________________________
help-smalltalk mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/help-smalltalk