Newsgroups: comp.lang.smalltalk
Path: cantaloupe.srv.cs.cmu.edu!das-news.harvard.edu!news2.near.net!MathWorks.Com!europa.eng.gtefsd.com!howland.reston.ans.net!EU.net!Germany.EU.net!donald!hmm
From: hmm@heeg.de (Hans-Martin Mosner)
Subject: Re: Curious bug in most versions of Smalltalk [SmalltalkAgents]
Message-ID: <CwqF5t.30H@heeg.de>
Organization: Georg Heeg Objektorientierte Systeme, Dortmund, FRG
References: <aaa90d38070210046280@[192.55.204.253]>
Date: Mon, 26 Sep 1994 10:15:29 GMT
Lines: 78

Without quoting the original articles (they've been quoted too much already)
I'd like to say that this is one of the areas where the semantics of
Smalltalk is not properly defined, and therefore the reactions of
different implementations are bound to be unexpected.
The proper semantics of super is this:

if B is a subclass of A, and
a method #foo is defined in B, and
#foo sends super #bar,
that the method search for #bar starts in A.

Now what if A is nil, as is the case in class Object (or any other 'root'
class), the behavior is undefined, since the method lookup mechanism
is only defined for classes.

What should the proper solution be?
I think that compile-time checking is useful as a hint, but not enough.
At least in some smalltalks, it is possible to change the superclass of
a class, so the compiler will not see anything weird when the method is
being defined, but it could still be executed with a nil superclass.

So there must be a runtime check.
Normally, method lookup is perceived as working somewhat like this:

lookupMethod: selectorSymbol startingAt: lookupClass
	| cls |
	cls := lookupClass.
	[(cls includesSelector: selectorSymbol)
		ifTrue: [^cls compiledMethodAt: selectorSymbol].
	cls := cls superClass.
	cls == nil] whileFalse.
	"doesNotUnderstand handling here"

If lookupClass is not a Behavior to start with, all kinds of undefined
things can happen.  But if we reformulate the lookup loop, the problem
goes (almost) away:

lookupMethod: selectorSymbol startingAt: lookupClass
	| cls |
	cls := lookupClass.
	[cls == nil] whileFalse: [
		(cls includesSelector: selectorSymbol)
			ifTrue: [^cls compiledMethodAt: selectorSymbol].
		cls := cls superClass].
	"doesNotUnderstand handling here"

So if lookupClass is nil (which would be the case when using super in
a root class) we immediately get a doesNotUnderstand.
However, we're still in trouble if the superclass of a class is a non-
Behavior object.  For the conceptual model, we could replace the check
[cls == nil] whileFalse: with [cls isBehavior] whileTrue:, and get clean
semantics.
This is, however, much too expensive to do in a VM.  Therefore I think
that it is acceptable to require that the user does not mess with the
superclass structure in such a way.  There are other places where the
VM just has to hope that the user cooperates and does not do things like
- anObject become: nil (with non-Digitalk become: semantics)
- put arbitrary byte values into structures serving as pointers
- create CompiledMethods that access inst vars beyond an object's size
- use primitives in classes where they don't apply
- add inst vars to Object
- ... you certainly know of other dirty things to do

From a theoretical standpoint, it is not possible to protect a sufficiently
complex self-modifying system against self-destruction (see GEB).
From an implementors standpoint, some theoretically possible safeguards
would make the system so slow that it's unusable.

I'd like to see the vendors adopt the second model of method lookup
so that portable class libraries can safely assume that nil superclasses
can be used without risking a system crash.

Hans-Martin
-- 
+--- Hans-Martin Mosner ---------------- <hmm@heeg.de> ---+
| These opinions are entirely ficticious.  Any similarity |
| to real opinions is purely coincidental and unintended. |
+---------------------------------------------------------+
