Global and pool variables, declaring and accessing non-local slots

2 views
Skip to first unread message

Klaus D. Witzel

unread,
Aug 18, 2008, 1:49:29 AM8/18/08
to Moebius project discussion, den...@iam.unibe.ch, stephane...@inria.fr, klaus....@cobss.com
For the SmallWorld tranformation from primordial soup (a running
example in another email thread) it was to decide where and how to
declare and access non-local slots. As is often the case one arrives
at a solution which Self has for this case: message sends :)

We already discussed in previous email that Moebius will use sort of
#bindingOf: from its <fixed methods> table and generate code for
dealing with what that returns.

How this works for the SmallWorld example, which uses #lookupSelector:
as alternative to #bindingOf:, can be base for the solution in Moebius
and its co-existence with ordinary Squeak classes and methods:

- at compile time the variable names are checked for existence, e.g.
that #lookupSelector: does not return nil, mainly for showing a
possible error message to the developer ;) and/or to allow for spell-
correcting the name :)

- at runtime the variable name is sent as selector to (self class) for
accessing its value.

In the Squeak VM at method lookup time when it finds that (thing
isCompiledMethod not) then the VM does:

thing run: originalSelector with: arguments in: originalReceiver

and the implementor of #run:with:in: gets the original receiver and
variable name (=> originalSelector) and can now do what it's pleased.

The simplest case is that #run:with:in: just returns (self) and can be
implemented by Squeak's Object class. This is the getter case, for
which the SmallWorld compiler emits

o push: self
o send: #class
o send: #symbol (variable name) with *no* argument

Of course in a co-existing Squeak .image the implementation of
#run:with:in: cannot just return self for every instance of Object,
therefore it does something which can be overrridden:

"method is #subclassResponsibility in Behavior"
^ originalReceiver accessing: originalSelector value: self

The not-sooo-simple case is an *assignment* to a non-local variable,
like (Processor := nil ;) for that the SmallWorld compiler emits this
setter:

o push: self
o send: #class
o evaluate the right-hand-side of :=
o send: #symbol (variable name) with *one* argument

The VM does its magic as before and the implementation of
#run:with:in: checks # of arguments and does (snippet ignores current
value (self) of the slot):

"method is #subclassResponsibility in Behavior"
^ originalReceiver assigning: originalSelector value: arguments first

The job of #assigning:value: is to locate the declarer on its own
superclass ladder => aSlotDeclarer in the SmallWorld example is the
class+method dictionary which holds the slot, and then it completes
the assignment:

^ aSlotDeclarer compiledMethodAt: aSymbol put: aValue

It is important to note that in the SqueakVM, slot-name-as-selector
sends are *not* subject of method caching (but all the other shown
methods are), therefore the above is not soo fast for performance
sensitive application with bytecode instruction set.

Another aspect is co-existence with existing Squeak classes and
methods (=> what we want to accomplish with Moebius): a system which
uses the above is running in its own namespace and module, and the
SmallWorld example compiler does *not* emit code which can access a
(Smalltalk at :#Smalltalk) variable.

Instead things must be declared in a module then they can be used by
+from entities in the module. A nice example is

myDeclarer "usually a class like Object"
compiledMethodAt: #Processor
put: (nil environment associationAt: #Processor)

I use the latter in the running SmallWorld example for avoiding name
clashes etc. SmallWorld has its own Class, Object, Parser, Collection,
Magnitude, etc with the same name symbols but other provenance as
those that exist in Squeak.

--------------

I hope the above has something useful for declaring and accessing non-
local slots in Moebius and for co-existence with Squeak?

Cheers,
Klaus

Igor Stasenko

unread,
Aug 18, 2008, 3:12:18 AM8/18/08
to moebius-proje...@googlegroups.com, den...@iam.unibe.ch, stephane...@inria.fr, klaus....@cobss.com
2008/8/18 Klaus D. Witzel <klaus....@cobss.com>:

.. erm.. why sending #class? Is this for possible overrides of #class method?
VM is rigid thing and its safer to have #class return same oop as if
VM does this primitively, otherwise its hard to imagine the number of
errors, which could be result of such override.

Not a big deal IMO.
A well written code should not use non-local values too often,
otherwise its not well-written code :)
And using a message sends instead of compiler early binding is much
more valuable than performance IMO.
As i heard, an early binding is nothing more than optimization and not
a part of smalltalk's design specs.

Btw, the speed loss could be reduced by implementing a dictionary
lookup with primitive, replacing #scanFor: method and leaving only
send #hash to searched object at language side.

> Another aspect is co-existence with existing Squeak classes and
> methods (=> what we want to accomplish with Moebius): a system which
> uses the above is running in its own namespace and module, and the
> SmallWorld example compiler does *not* emit code which can access a
> (Smalltalk at :#Smalltalk) variable.
>
> Instead things must be declared in a module then they can be used by
> +from entities in the module. A nice example is
>
> myDeclarer "usually a class like Object"
> compiledMethodAt: #Processor
> put: (nil environment associationAt: #Processor)
>
> I use the latter in the running SmallWorld example for avoiding name
> clashes etc. SmallWorld has its own Class, Object, Parser, Collection,
> Magnitude, etc with the same name symbols but other provenance as
> those that exist in Squeak.
>
> --------------
>
> I hope the above has something useful for declaring and accessing non-
> local slots in Moebius and for co-existence with Squeak?
>

Well..

Is it a SmallWorld's design decision to keep pool vars in class dictionaries?
I find sending #bindingOf: is more clean, logical and less
complicated/intrusive (consider browser which expects only methods
returned by #methodDict message).
I really don't like abusing things which was designed for different purpose(s).

> Cheers,
> Klaus
>
> >
>


--
Best regards,
Igor Stasenko AKA sig.

Klaus D. Witzel

unread,
Aug 18, 2008, 6:31:22 AM8/18/08
to moebius-proje...@googlegroups.com, den...@iam.unibe.ch, stephane...@inria.fr
On Mon, 18 Aug 2008 09:12:18 +0200, Igor Stasenko wrote:

> 2008/8/18 Klaus D. Witzel :


>>
>> For the SmallWorld tranformation from primordial soup (a running
>> example in another email thread) it was to decide where and how to
>> declare and access non-local slots. As is often the case one arrives
>> at a solution which Self has for this case: message sends :)

...


>> In the Squeak VM at method lookup time when it finds that (thing
>> isCompiledMethod not) then the VM does:
>>
>> thing run: originalSelector with: arguments in: originalReceiver
>>
>> and the implementor of #run:with:in: gets the original receiver and
>> variable name (=> originalSelector) and can now do what it's pleased.
>>
>> The simplest case is that #run:with:in: just returns (self) and can be
>> implemented by Squeak's Object class. This is the getter case, for
>> which the SmallWorld compiler emits
>>
>> o push: self
>> o send: #class
>
> .. erm.. why sending #class?

Just did that for illustration, using existing Smalltalk things for
communication. Moebius might change that, NP.

> Is this for possible overrides of #class method?

A-b-s-o-l-u-t-e-l-y not ;)

> VM is rigid thing and its safer to have #class return same oop as if
> VM does this primitively, otherwise its hard to imagine the number of
> errors, which could be result of such override.

This remark makes me a bit concerned that we're not talking about
something similiar. Example

igorMethod
"answer the value of a non-local variable (slot)"
^ self class SlotName

and the above bytecodes equiv this source method representation, Okay?

>> o send: #symbol (variable name) with *no* argument

...


>> It is important to note that in the SqueakVM, slot-name-as-selector
>> sends are *not* subject of method caching (but all the other shown
>> methods are), therefore the above is not soo fast for performance
>> sensitive application with bytecode instruction set.
>>
>
> Not a big deal IMO.

:)

> A well written code should not use non-local values too often,
> otherwise its not well-written code :)
> And using a message sends instead of compiler early binding is much
> more valuable than performance IMO.

And all jitted and/or partly compiled VMs/CREs compare sort of equal at
this level :) Only Smalltalk with its (Association key: slotName value:
slotValue) is an exception here, because of VM's immediate bytecode
support.

> As i heard, an early binding is nothing more than optimization and not
> a part of smalltalk's design specs.

:) anything special you could point me to (a paper or an article so), for
my collection of Things Done Right?

> Btw, the speed loss could be reduced by implementing a dictionary
> lookup with primitive, replacing #scanFor: method and leaving only
> send #hash to searched object at language side.

Good point, thank you.

...


>> --------------
>>
>> I hope the above has something useful for declaring and accessing non-
>> local slots in Moebius and for co-existence with Squeak?
>>
>
> Well..
>
> Is it a SmallWorld's design decision to keep pool vars in class
> dictionaries?

No, not separate class dictionaries (possibly misunderstood?), it's
instead in the method dictionary of the declaring class.

> I find sending #bindingOf: is more clean, logical and less
> complicated/intrusive (consider browser which expects only methods
> returned by #methodDict message).

Especially when does browser send #bindingOf: or access method dictionary
directly? I'd love to know every single occurence, since that would incur
*much* more work for me :(

No; browsers do filter, at their indirect accessing level, the method
dictionary through the class' organization object. And this is good so :)

But furtunately I know of no tools which do any #binding or direct method
access -- all they do is use what's implemented from ClassDecription
protocol downwards to more special subclasses of ClassDecription
(exceptions are stupid things like #traitsComposition: in Behavior etc,
but that are exceptions).

Will still need to check what SystemNavigation helpers want from
CompiledMethods (like messages, senders, etc), some of them like
#whichSelectorsReferTo:* miss the #isCompiledMethod check, but this has no
priority for me at the moment (noticed that #selectorsAndMethodsDo: seems
to the central point where check was forgotten by the VM gods).

Nevertheless, if #isCompiledMethod would not be retrofitted into helpers
then #run:with:in: would be great success for the VM maintainers :) only
hard work for nothing :(

Meanwhile I have collected three dozens of methods for the SmallWorld baby
classes (who themselves do not inherit anything from Behavior and its
subclasses, after #new #new #new ;) Many/most of these methods are related
to me wanting inspection and debugging and recompilation (all is done to
the strange-to-Squeak environment, with existing tools).

So far, Marcus' methods in Behavior are best, easy reusable for things
which don't inherit from big Behavior brother ;)

> I really don't like abusing things which was designed for different
> purpose(s).

-
http://www.google.com/search?q=ObjectsAsMethods+VM+site:lists.squeakfoundation.org

No, #run:with:in: was intentionally designed into the Squeak system and is
fully supported by the various VM's, don't worry ;)

>> Cheers,
>> Klaus
>>
>> >>
>>
>
>


Igor Stasenko

unread,
Aug 18, 2008, 8:26:28 AM8/18/08
to moebius-proje...@googlegroups.com, den...@iam.unibe.ch, stephane...@inria.fr
2008/8/18 Klaus D. Witzel <klaus....@cobss.com>:
>
Sorry, yes, i meant method dictionary of class.

>> I find sending #bindingOf: is more clean, logical and less
>> complicated/intrusive (consider browser which expects only methods
>> returned by #methodDict message).
>
> Especially when does browser send #bindingOf: or access method dictionary
> directly? I'd love to know every single occurence, since that would incur
> *much* more work for me :(
>

I think 'explain' command should show how browser get know if given
identifier is temp/inst/class/pool/global var.
I just can suspect that some usages of #compiledMethodAt: would not
expect having non-compiled method in return. And this is why i against
messing methods with pool vars.
Of course it is completely a design related problem. One could
implement class hierarchy, browser & compiler which aware that classes
using single dictionary for holding both methods and class vars.
In essence this can be seen as generalization, that each class having
own namespace (symbol->object). Or if look from Self's POV it is
nothing more than just a named slots of class object :)

> No; browsers do filter, at their indirect accessing level, the method
> dictionary through the class' organization object. And this is good so :)
>

Maybe so. I'm not very good with protocols which is correct for usage
to work with meta data such as methods & behavior.
Do not blame me for using the methods i find most convenient fro my
purposes in Behavior protocol for metaprogramming. :)

Yes yes. I'm aware of that. I'm just thinking that this is an overkill.
The #run:with:in: is sent with
run: oldSelector with: arguments in: aReceiver
^self perform: oldSelector withArguments: arguments

which means that VM need to allocate an array oop for arguments.
While if you make compiler intrinsic to generate #bindingOf:{put:}
send sequence - you don't have this overhead. And receiver gets all
required arguments in the same way, just with different selector :)

Btw, squeak's #bindingOf: returns an association for given symbol,
while in above - #bindingOf: / #bindingOf:put: - should retrieve or
store value instead.

Maybe its better to use #bindingAt:{put:} to avoid confusion.

------------

Some more thoughts about use of #run:with:in:

Since class vars could be used to hold any value, and Squeak VM
sending a #run:with:in: message to this object, you'll have a problem,
that you need to define #run:with:in: in Object class (or even in
ProtoObject) in SmallWorld's image and should prevent it from
overrides.
Its also makes very problematic to use #run:with:in: anywhere except
for non-local symbol lookup:

for example, you having a class var named CoolVar and put there an
object with overridden #run:with:in: then you may not have what you
expecting:

| obj |
CoolVar := nil. "initially is nil"
self assert: (CoolVar == nil). " do not fail, since
Object>>run:with:in: works well and returns self"

obj := ClassWithOverriddenRunWithIn new.

CoolVar := obj.
self assert: (CoolVar == obj). "will fail, if override does not returns self"

CoolVar := nil. "this one can fail as well, since
ClassWithOverriddenRunWithIn>>run:with:in can simply ignore extra
argument"

This leads to a problem that assignments may not always work as
assignments - a big source of errors and confusion.

That's why "I really don't like abusing things which was designed for
different purpose(s)" :)


P.S.

Btw, maybe i was not attentive reading your post, how you suppose to
deal with class vars use at class side?
It is certain that since class vars seen both at instance side and at
class side, they should have single place holder.
But then compiler should be aware that method compiled for class side,
to generate:

o push: self


o send: #symbol (variable name) with *no* argument

instead of

o push: self
o send #clas


o send: #symbol (variable name) with *no* argument

at instance side.

Or maybe you dealing with it at run-time in #run:with:in: method(s)?

Klaus D. Witzel

unread,
Aug 18, 2008, 11:27:32 AM8/18/08
to moebius-proje...@googlegroups.com, Marcus Denker, stephane...@inria.fr
On Mon, 18 Aug 2008 14:05:55 +0200, Marcus Denker wrote:

> In the past many people wanted to override #class. E.g. for introducing
> proxies where #class returns
> the class of the proxied object, this can be very useful.

Interesting point. Perhaps some of them complained before it became clear
that regular compilers emits the special selector bytecode (no lookup) for
#class ;)

BTW and a bit OT: when transferring SmallWorld bytecode generation to
Squeak I found early steps of block closures, I first thought that was a
bug 8-)

But I took the idea and based copying of unshareable block temps on the
regular Squeak bytecode set, so no need for a VM patch nor message sends
nor allocation of thing(s) other than GOF BlockContext (and blocks have
again same method as home has). Decompiler must + can recognize this, too.

It looks so easy that I asked myself, why haven't people used that for
decade(s), cross-VM version + platform compatible (no crash with .images
from different un/-supporting VMs).

Could this find use in NewCompiler?

Cheers,
Klaus

> Marcus
>
>
> --
> Marcus Denker -- den...@iam.unibe.ch
> http://www.iam.unibe.ch/~denker
>


Klaus D. Witzel

unread,
Aug 18, 2008, 1:40:58 PM8/18/08
to moebius-proje...@googlegroups.com, Marcus Denker, stephane...@inria.fr
On Mon, 18 Aug 2008 18:24:58 +0200, Marcus Denker wrote:

> Klaus wrote:
>> Interesting point. Perhaps some of them complained before it became
>> clear that regular compilers emits the special selector bytecode (no
>> lookup) for #class ;)
>>
>

> They complained and then changed the compiler to emit a message send for
> #class.

:)

>> BTW and a bit OT: when transferring SmallWorld bytecode generation to
>> Squeak I found early steps of block closures, I first thought that was
>> a bug 8-)
>>

> ?

The SmallWorld compiler pulls all temps/block args used, at block-scope
end compile-time, out of scope for any other block onwards-in-time. So it
doesn't reuse any slot for any other block again, regardless of level.

[this in contrast to Squeak compiler (since St80?) who make the same name
get the same slot, if level permits. So people attempt to make new
bytecodes for better closure support].

Now what SmallWorld compiler does not do is, to copy unshared values into
declarer scope of a nested block. This was easy to add when I saw that
little "sin".

>> But I took the idea and based copying of unshareable block temps on the
>> regular Squeak bytecode set, so no need for a VM patch nor message
>> sends nor allocation of thing(s) other than GOF BlockContext (and
>> blocks have again same method as home has). Decompiler must + can
>> recognize this, too.
>>
>> It looks so easy that I asked myself, why haven't people used that for
>> decade(s), cross-VM version + platform compatible (no crash with
>> .images from different un/-supporting VMs).
>>
>> Could this find use in NewCompiler?
>>
>

> Maybe, I can not really understand much from this short explanation, I
> have to admit.

Could go down to explainig what bytecodes used and when, but I will save
the subject for the next meeting with you (Camp Smalltalk over the
weekend, perhaps?).

First it must be decided whether a platform-neutral, .image version
neutral, and VM neutral solution is wanted and if there are other people
interested in maintaining things.

Today NewCompiler can generate VM-dependent bytecodes and/or alternatively
encode message sends for BlockClosure handling. My suggestion goes go into
the third direction, to not do both.

Cheers,
Klaus

Klaus D. Witzel

unread,
Aug 18, 2008, 4:42:24 PM8/18/08
to Moebius project discussion
On Aug 18, 2:26 pm, Igor Stasenko wrote:

[ when posting parts only here, the cc'd correspondends cannot
neccessarily follow the thread :(
put moebius list in to: and correspondents in cc:, that works,
example the 2 other responses ]

> 2008/8/18 Klaus D. Witzel :
...
> I think 'explain' command should show how browser get know if given
> identifier is temp/inst/class/pool/global var.

:) so explain must be reviewed, thanks :)

> I just can suspect that some usages of #compiledMethodAt: would not
> expect having non-compiled method in return.

No, user of #compiledMehodAt: know precisely what they do, since that
method does #compiledMehodAt:ifAbsent: (BTW here #isCompiledMethod is
not asked as well); so

}}compiledMethodAt: sel "revised"

(answer := self compiledMethodAt: sel ifAbsent: nil) isCompiledMethod
ifTrue: [^ anwer]
ifFalse: ["do the exception as before"]

> And this is why i against
> messing methods with pool vars.

:( are you criticising Self for the mess because it works that way :(

> Of course it is completely a design related problem.

Right.

> One could
> implement class hierarchy, browser & compiler which aware that classes
> using single dictionary for holding both methods and class vars.

:)

> In essence this can be seen as generalization, that each class having
> own namespace (symbol->object). Or if look from Self's POV it is
> nothing more than just a named slots of class object :)

Exactly.

> > No; browsers do filter, at their indirect accessing level, the method
> > dictionary through the class' organization object. And this is good so :)
>
> Maybe so. I'm not very good with protocols which is correct for usage
> to work with meta data such as methods & behavior.
> Do not blame me for using the methods i find most convenient fro my
> purposes in Behavior protocol for metaprogramming. :)

Heh, never would do so :)
...
> >> I really don't like abusing things which was designed for different
> >> purpose(s).
>
> > -
> >http://www.google.com/search?q=ObjectsAsMethods+VM+site:lists.squeakf...
>
> > No, #run:with:in: was intentionally designed into the Squeak system and is
> > fully supported by the various VM's, don't worry ;)
>
> Yes yes. I'm aware of that. I'm just thinking that this is an overkill.
> The #run:with:in: is sent with
> run: oldSelector with: arguments in: aReceiver
>         ^self perform: oldSelector withArguments: arguments
>
> which means that VM need to allocate an array oop for arguments.

It would be no difference when DNU would be used instead of
#run:with:in:, but a big difference when #bindingOf: would be used ;(

> While if you make compiler intrinsic to generate #bindingOf:{put:}
> send sequence - you don't have this overhead. And receiver gets all
> required arguments in the same way, just with different selector :)

I cannot follow this one without concrete example what non-head you
have :(

> Btw, squeak's #bindingOf: returns an association for given symbol,
> while in above - #bindingOf: / #bindingOf:put: - should retrieve or
> store value instead.

I don't like that. Nowadays compiler puts binding *as*is* in a literal
slot of a method, fullest encapsulation, object-in => object-out.

Other users of #bindingOf: who know what static (javaneese-thinking)
type is returned, are abusers :(

> Maybe its better to use #bindingAt:{put:} to avoid confusion.

Please not. Compile-Time (static #bindingOf:) ~= Run-Time (dynamic
message sent).

> ------------
>
> Some more thoughts about use of #run:with:in:
>
> Since class vars

Non-local slots?

> could be used to hold any value, and Squeak VM
> sending a #run:with:in: message to this object, you'll have a problem,
> that you need to define #run:with:in: in Object class (or even in
> ProtoObject)

No, I have it in Object where it belongs. Also, SmallWorld (like
Moebius) has its own namespace in an own module which does not see
Squeak and vice-versa (did I tell you the glue which links two/more
such mutually invisible modules).

So, I should not care who implements #run:with:in: in the Squeak
module (in the rest of the world) or whether it DNU's in Squeak, or
not.

> in SmallWorld's image and should prevent it from overrides.

Now this is a good one ! How would you prevent that?

> Its also makes very problematic to use #run:with:in: anywhere except
> for non-local symbol lookup:

Non-local symbol lookup does not parse: ? the symbol is non-local ?
the lookup is non-local ? the result is non-local ?

> for example, you having a class var

Non-local variable?

> named CoolVar and put there an
> object with overridden #run:with:in: then you may not have what you
> expecting:
>
> | obj |
> CoolVar := nil. "initially is nil"
> self assert: (CoolVar == nil).  " do not fail, since
> Object>>run:with:in: works well and returns self"
>
> obj := ClassWithOverriddenRunWithIn new.

I do not understand one bit; do not know the any definitions of these;
cannot digest it :(

> CoolVar := obj.
> self assert: (CoolVar == obj). "will fail, if override does not returns self"
>
> CoolVar := nil. "this one can fail as well, since
> ClassWithOverriddenRunWithIn>>run:with:in can simply ignore extra
> argument"
>
> This leads to a problem that assignments may not always work as
> assignments - a big source of errors and confusion.
>
> That's why "I really don't like abusing things which was designed for
> different purpose(s)" :)

Then please do not abuse compile-time #bindingOf: for run-time jobs
and do not give that method the interpretation that it returns a
(sub-)instance of Association?

> P.S.
>
> Btw, maybe i was not attentive reading your post, how you suppose to
> deal with class vars use at class side?

Send a name (a selector) to an object?

> It is certain that since class vars seen both at instance side and at
> class side, they should have single place holder.

I'm concerned you care where these slots are stored. First:

o temps and args are local to a context
o self is local to a context
o instance variables are local to a context

=> nothing else can be local, so must be non-local.

Second: now the instance side (which inherits!) does not have the name
you want (say, #IgorVar), so the class side is the next natural point
of service?

IMO: yes, you cannot send non-local names to local entities declared
above, since a local thing isn't also a non-local thing at the same
time. This is rather a matter of discipline (n00b friendly as well),
not an implementation issue.

> But then compiler should be aware that method compiled for class side,
> to generate:
>
> o push: self

o push: theNonMetaClass ? (as is in Smalltalk today, all classes know
who that is)

> o send: #symbol (variable name) with *no* argument
>
> instead of
>
> o push: self
> o send #clas

o push: theNonMetaClass ? (as is in Smalltalk today, all classes know
who that is)

> o send: #symbol (variable name) with *no* argument
>
> at instance side.
>
> Or maybe you dealing with it at run-time in #run:with:in: method(s)?

No, #run:with:in: has insufficient knowledge why it must do what it
must to do, can only decide "getter/setter" (0 args, 1 args) and all
other # args => DNU

Igor Stasenko

unread,
Aug 18, 2008, 9:42:18 PM8/18/08
to moebius-proje...@googlegroups.com
2008/8/18 Klaus D. Witzel <klaus....@cobss.com>:
>
> On Aug 18, 2:26 pm, Igor Stasenko wrote:
>
> [ when posting parts only here, the cc'd correspondends cannot
> neccessarily follow the thread :(
> put moebius list in to: and correspondents in cc:, that works,
> example the 2 other responses ]
>
>> 2008/8/18 Klaus D. Witzel :
> ...
>> I think 'explain' command should show how browser get know if given
>> identifier is temp/inst/class/pool/global var.
>
> :) so explain must be reviewed, thanks :)
>
>> I just can suspect that some usages of #compiledMethodAt: would not
>> expect having non-compiled method in return.
>
> No, user of #compiledMehodAt: know precisely what they do, since that
> method does #compiledMehodAt:ifAbsent: (BTW here #isCompiledMethod is
> not asked as well); so
>
> }}compiledMethodAt: sel "revised"
>
> (answer := self compiledMethodAt: sel ifAbsent: nil) isCompiledMethod
> ifTrue: [^ anwer]
> ifFalse: ["do the exception as before"]
>
>> And this is why i against
>> messing methods with pool vars.
>
> :( are you criticising Self for the mess because it works that way :(
>

No! I just saying that smalltalk design is different and intended to
hold only methods in method dictionaries.

Let me elaborate.

Object subclass: #SomeClass classVariableNames: 'CoolVar'.

SomeClass>>method1
CoolVar := nil.
self assert: (CoolVar == nil)

SomeClass>>setCoolVarWithInstanceOf: aClass
| obj |
obj := aClass new.


CoolVar := obj.
self assert: (CoolVar == obj)

---

Now suppose a test case:

myObj := SomeClass new.
myObj method1.
myObj setCoolVarWithInstanceOf: ClassWithOverriddenRunWithIn.
myObj method1.

now look what happens, if you using a #run:with:in: pattern:
- the receiver of #run:with:in: is the object held in value slot of
association (where VM expects a CompiledMethod instance). And behavior
of accessing the CoolVar depends on the current value object!

In contrary, if you using a #bindingOf: , then receiver of
#bindingOf: is a SomeClass - and behavior of accessing CoolVar always
depends only from SomeClass.


Next issue is difference between:

SomeClass>>coolVar
^ CoolVar

and:

SomeClass class>>coolVar
^ CoolVar

at instance-side, everything is OK:
compiler generates:
push self (SomeClass instance)
send class
send #CoolVar (receiver is SomeClass)

and VM will do lookup in SomeClass method dictionary.

but if you apply same operations to a class side, it will go wrong:
push self (SomeClass )
send class
send #CoolVar (SomeClass class -- its metaclass)

here VM will do lookup for #CoolVar method in metaclass methods
dictionary, not in a SomeClass method dictionary.

So, in first case, a receiver of #CoolVar is SomeClass class object,
and in second case , a receiver of #CoolVar is SomeClass metaclass object.

Here the problem - you looking for same symbol in different hierachies
of method dictionaries, unless you make compiler to not send #class in
a class-side methods.

Or you can deal with it at run time. But at run time , i think you
will need to handle DNU in one of the cases, otherwise you need to
have to keep (#CoolVar -> object) associations in both class and
metaclass dictionaries and synchronize them at assignment.

Klaus D. Witzel

unread,
Aug 19, 2008, 12:16:30 AM8/19/08
to Moebius project discussion
On Aug 19, 3:42 am, Igor Stasenko wrote:
> 2008/8/18 Klaus D. Witzel :
...
> > o push: theNonMetaClass ? (as is in Smalltalk today, all classes know
...
> >> Or maybe you dealing with it at run-time in #run:with:in: method(s)?
>
> > No, #run:with:in: has insufficient knowledge why it must do what it
> > must to do, can only decide "getter/setter" (0 args, 1 args) and all
> > other # args => DNU
>
> Let me elaborate.

Thanks, you make things clearer with it.

> Object subclass: #SomeClass  classVariableNames: 'CoolVar'.
>
> SomeClass>>method1
>   CoolVar := nil.
>   self assert: (CoolVar == nil)
>
> SomeClass>>setCoolVarWithInstanceOf: aClass
>    | obj |
>    obj := aClass new.
>    CoolVar := obj.
>    self assert: (CoolVar == obj)
> ---
>
> Now suppose a test case:
>
> myObj := SomeClass new.
> myObj method1.
> myObj setCoolVarWithInstanceOf: ClassWithOverriddenRunWithIn.
> myObj method1.
>
> now look what happens, if you using a #run:with:in: pattern:
> - the receiver of #run:with:in: is the object held in value slot of
> association (where VM expects a CompiledMethod instance). And behavior
> of accessing the CoolVar depends on the current value object!

Right.

> In contrary, if you using a #bindingOf: , then receiver of
> #bindingOf: is a SomeClass  - and behavior of accessing CoolVar always
> depends only from SomeClass.

Right. That's why we noted earlier that #run:with:in: belongs into
Object (of your module) when used. BTW in Moebious we don't have to
follow what VM does, we' would have the cause of #run:with:in: but not
necessary the slowish #run:with:in: implementation.

> Next issue is difference between:
>
> SomeClass>>coolVar
>   ^ CoolVar
>
> and:
>
> SomeClass class>>coolVar
>   ^ CoolVar
>
> at instance-side, everything is OK:
> compiler generates:
> push self (SomeClass instance)
> send class
> send #CoolVar (receiver is SomeClass)

Recall this

o push: theNonMetaClass

To do that, it takes a decision step; say compiler has 'class' in its
own iVars (class against which it compiles):

o push: self "unconditionally"

(class theNonMetaClass == class)
ifTrue: [o send: #class]
ifFalse: ["do nothing"].

> and VM will do lookup in SomeClass method dictionary.

Right.

> but if you apply same operations to a class side, it will go wrong:
> push self (SomeClass )

No, since:

> send class

precisely this send does not happen. I posted earlier that is does

o push: theNonMetaClass

I'm sorry if that was unclear, theNonMetaClass concept+method answer
is an invariant in Smalltalk, a constant which derives you with whom
you are talking.

When you make a decision on (class theNonMetaClass == class) you know
precisely whom you talk to. In a 3.10.2 stock .image for example,
there are 68 senders of #theNonMetaClass who obtain that knowledge/
invariant. This must be so otherwise navigating the browser would be
far away from being convenient.

> send #CoolVar (SomeClass class -- its metaclass)
>
> here VM will do lookup for #CoolVar method in metaclass methods
> dictionary, not in a SomeClass method dictionary.

Does not happen. Always recall this: you cannot just take an instance
of CompiledMethod from the class side and #at:put: it at the instance
side (nor into the other direction), this does not happen (unconsious
mistakes notwithstanding ;)

And [OT] people do not want this to happen even when the
CompiledMethod is stateless traits -- they want class side and
instance side to be separable at any cost. Still OT: stateless traits
has no belonging to any "side" but with this I am sooo lonely on this
our planet ;)

> So, in first case, a receiver of #CoolVar is SomeClass class object,
> and in second case , a receiver of #CoolVar is SomeClass metaclass object.
>
> Here the problem - you looking for same symbol in different hierachies
> of method dictionaries, unless you make compiler to not send #class in
> a class-side methods.

Yeah, only I thought when I just mention theNonMetaClass then things
will be clearer for you. Apologies for not making that more obvious
earlier.

> Or you can deal with it at run time.

No intention for doing so.

Igor Stasenko

unread,
Aug 19, 2008, 1:31:08 AM8/19/08
to moebius-proje...@googlegroups.com
2008/8/19 Klaus D. Witzel <klaus....@cobss.com>:

This would force increase in complexity of polymorphic send mechanism.
There must be a balance between the size of native code , inlined to
perform a send, and flexibility.

To avoid inlining too much code at polymorphic send site (causing
native methods become too big)
we need to put most of message send handling code in separate method(s).

Currently compiler calls a method which responsible for binding
message selector to method - and then simply calls returned method.
This means, that compiler assuming that returned value of #bind:
method is always an instance of CompiledMethod.

To handle method-as-arbitrary-object case, the bind method then should
check if result of lookup is not a method oop. And if not - then do
additional lookup and return #run:with:in: method which should be
called.

But it is easy to say than just do. The complication is that
#run:with:in: requiring different arguments than original message, so
before sending a message , it would require to make changes on stack
and move original arguments to array.

It is also interesting, how to determine if given oop is
CompiledMethod instance or not (remember , we can't send
#isCompiledMethod in the code responsible for polymorphic send -
otherwise we end up with a mess that to perform ANY polymorphic send
we need to do another polymorphic send :).

I think that its better to keep things simple - allow only
CompiledMethods (or subclasses) in method dicts, and provide other
means for redirecting/wrapping message sends.

Also, some languages, like Io allow to redefine activation action for a method.
It makes things very flexible, and horribly slow at the same time ,
because to make a simplest polymorphic send you must send #activate to
method oop, again, polymorphically :)

[snipped the rest, since we agreed ]

Klaus D. Witzel

unread,
Aug 19, 2008, 5:03:39 AM8/19/08
to Moebius project discussion
On Aug 19, 7:31 am, Igor Stasenko wrote:
> 2008/8/19 Klaus D. Witzel :
...
> >> Now suppose a test case:
...
> >> now look what happens, if you using a #run:with:in: pattern:
> >> - the receiver of #run:with:in: is the object held in value slot of
> >> association (where VM expects a CompiledMethod instance). And behavior
> >> of accessing the CoolVar depends on the current value object!
>
> > Right.
>
> >> In contrary, if you using a #bindingOf: , then receiver of
> >> #bindingOf: is a SomeClass  - and behavior of accessing CoolVar always
> >> depends only from SomeClass.
>
> > Right. That's why we noted earlier that #run:with:in: belongs into
> > Object (of your module) when used. BTW in Moebious we don't have to
> > follow what VM does, we' would have the cause of #run:with:in: but not
> > necessary the slowish #run:with:in: implementation.
>
> This would force increase in complexity of polymorphic send mechanism.

No. This would force you think about objects, and nothing but
objects ;) I understand and appreciate your argument, but you are
going to partly replace (corrupt was your word) a *running* VM and you
*are* obliged to take care of the user as before and even better.

Nothing stops a Smalltalk user from doing this

- http://bugs.squeak.org/view.php?id=7157

> There must be a balance between the size of native code , inlined to
> perform a send, and flexibility.

No, there must be all error preventions which are in the current VM
(even those which are currently missing). If that is not possible
(your balance argument) then Moebius would be infeasible :(

This argument has nothing to do with possible use for ObjectAsMethod
as potential cause, no this is completely independent because all you
find at run-time is oop's.

> To avoid inlining too much code at polymorphic send site (causing
> native methods become too big)

Then Mobius could not supprt the checks of the VM it replaces :(

> we need to put most of message send handling code in separate method(s).
>
> Currently compiler calls a method which responsible for binding
> message selector to method - and then simply calls returned method.

NB compiler is not the VM, compiler has more time and intention for
checking things and informing its user ;)

> This means, that compiler assuming that returned value of #bind:
> method is always an instance of CompiledMethod.

of CompiledMethod? of Object ! you are at run-time now, impossible to
complain about oops you find, since the developer is gone already !

> To handle method-as-arbitrary-object case, the bind method then should
> check if result of lookup is not a method oop.

Let the object decide that by itself, have the compiler send something
to it.

> And if not - then do
> additional lookup and return #run:with:in: method which should be
> called.

No, nothing needs additional lookup. A <fixed methods> in vtable must
take this responsibility at run-time.

> But it is easy to say than just do. The complication is that
> #run:with:in: requiring different arguments than original message, so
> before sending a message , it would require to make changes on stack
> and move original arguments to array.

Don't worry about #run:with:in: because for the compiler this does
*not* exist at all. When your lambda compilation does its inlining
magic, it wants methods, so as a well behaving and predictable citizen
it filters (anObject isCompiledMethod) like any other well behaving
citizen does.

Recall that Moebius emits the hw-instruction set and that is another
level of responsibility than the Squeak compiler and its VM nowadays
have ...

> It is also interesting, how to determine if given oop is
> CompiledMethod instance or not (remember , we can't send
> #isCompiledMethod in the code responsible for polymorphic send -

As noted ealier, if you cannot take over this and other
responsibilities from the to-be corrupted VM then you would face an
infeasibility problem :(

Just duplicate Squeak VM's #isCompiledMethod: (there are plenty of
other such ground base things which *must* be duplicated from Squeak's
Interpreter and ObjectMemory implementation). Otherwise, no common
language => no communication :(

> otherwise we end up with a mess that to perform ANY polymorphic send
> we need to do another polymorphic send :).

No, not needed, as written above.

> I think that its better to keep things simple - allow only
> CompiledMethods (or subclasses) in method dicts, and provide other
> means for redirecting/wrapping message sends.

I prefer Moebius to *do* things and not to *redirect* or *wrap*
things. If that (doing) would not be possible then why would someone
use Moebius instead of, the existing Squeak VM which has indeed the
capability that it can be changed and compiled?

> Also, some languages, like Io allow to redefine activation action for a method.
> It makes things very flexible, and horribly slow at the same time ,
> because to make a simplest polymorphic send you must send #activate to
> method oop, again, polymorphically :)

:)

> [snipped the rest, since we agreed ]

:-D

Igor Stasenko

unread,
Aug 19, 2008, 5:25:07 AM8/19/08
to moebius-proje...@googlegroups.com
2008/8/19 Klaus D. Witzel <klaus....@cobss.com>:
>

We can do something similar, i think. Instead of doing direct call of
method oop native code,
we could use the fixedMethodsTable to call #activate method for method oop.
So, a CompiledMethod will override an #activate method - to simply
call its entry point, while by default, for the rest of objects in
system, an #activate will result in sending #run:with:in: or something
similar.

So, a procedure of sending message to object could be changed to:

method := receiver callBind: selector numArgs: ... "does lookup &
returns a method to handle message"
result := method callActivateMethod "activates a method"

a callXXX are special natively inlined methods, which doing following:

receiver vtable fixedMethodsTable someFixedMethod call.

This makes things simple, error proof and w/o "idiotic" run-time
checking #isCompiledMethod - everything is defined through behavior.

Klaus D. Witzel

unread,
Aug 19, 2008, 11:07:44 AM8/19/08
to Moebius project discussion
On Aug 19, 11:25 am, Igor Stasenko wrote:
> 2008/8/19 Klaus D. Witzel :
...
> > I prefer Moebius to *do* things and not to *redirect* or *wrap*
> > things. If that (doing) would not be possible then why would someone
> > use Moebius instead of, the existing Squeak VM which has indeed the
> > capability that it can be changed and compiled?
>
> >> Also, some languages, like Io allow to redefine activation action for a method.
> >> It makes things very flexible, and horribly slow at the same time ,
> >> because to make a simplest polymorphic send you must send #activate to
> >> method oop, again, polymorphically :)
>
> We can do something similar, i think. Instead of doing direct call of
> method oop native code,
> we could use the fixedMethodsTable to call #activate method for method oop.
> So, a CompiledMethod will override an #activate method - to simply
> call its entry point, while by default, for the rest of objects in
> system, an #activate will result in sending #run:with:in: or something
> similar.

Hey, native objects with specialization :)

> So, a procedure of sending message to object could be changed to:
>
> method := receiver callBind: selector numArgs: ...  "does lookup &
> returns a method to handle message"
> result := method callActivateMethod  "activates a method"

Ahh, ohh, this now looks like my threaded bytecode VM looked at that
point, which I wrote in Smalltalk and *.c in parallel and tried to
find out how call sites of *methods*not*callees* can be charged branch
misprediction :)

I found the same calling sequence like yours above:

o obtain the method for a selector and things *)
o jcc away when (your #callBind:) dictated so (mispredicton charged
here)
o activate the method by call (e.g. the CPU's %eip% return address)
....
o away: return (pops things so stack is clear)

*) or invoke things like, #primitiveAdd with precisely the same
calling sequence but jcc just around its method activation when
integer arithmethic was successful, etc, etc. always a triplet #(call
jcc call).

> a callXXX are special natively inlined methods, which doing following:
>
> receiver vtable fixedMethodsTable someFixedMethod call.
>
> This makes things simple, error proof and w/o "idiotic" run-time
> checking #isCompiledMethod - everything is defined through behavior.

great ! :) sending messages rules :)

Igor Stasenko

unread,
Aug 19, 2008, 1:21:08 PM8/19/08
to moebius-proje...@googlegroups.com
2008/8/19 Klaus D. Witzel <klaus....@cobss.com>:
>

One of the reasons why i prefer to work in team - to save me from own
stupidity and to share with knowledge and evaluate approaches before
wasting time implementing crappy stuff :)

> o obtain the method for a selector and things *)
> o jcc away when (your #callBind:) dictated so (mispredicton charged
> here)
> o activate the method by call (e.g. the CPU's %eip% return address)
> ....
> o away: return (pops things so stack is clear)
>
> *) or invoke things like, #primitiveAdd with precisely the same
> calling sequence but jcc just around its method activation when
> integer arithmethic was successful, etc, etc. always a triplet #(call
> jcc call).
>
>> a callXXX are special natively inlined methods, which doing following:
>>
>> receiver vtable fixedMethodsTable someFixedMethod call.
>>
>> This makes things simple, error proof and w/o "idiotic" run-time
>> checking #isCompiledMethod - everything is defined through behavior.
>
> great ! :) sending messages rules :)

yeah, with one exception, that you can't put a non-CompiledMethod oop
in fixedMethodsTable.
But i think we can live with such limitation :)

Reply all
Reply to author
Forward
0 new messages