[squeak-dev] Environments: imported bindings overwriting each other

David T. Lewis lewis at mail.msen.com
Sun Jan 22 01:21:42 UTC 2017

This certainly sounds like a good change to me, especially since it is
supported by unit tests.


On Sun, Jan 22, 2017 at 01:19:18AM +0100, Jakob Reschke wrote:
> Hi all,
> I stumbled on something with Environment imports and would like to
> hear other opinions about the issue.
> Currently, environments do not care all that much about other
> environments declaring the same name. If an environment T imports from
> environments A, B, C, the binding of #x in T will be replaced with the
> binding of #x that was added last in any of the environments A, B, C,
> or T.
> Example:
>     env1 := Environment new.
>     env2 := Environment new.
>     env1 exportSelf.
>     env2 importSelf.
>     env2 import: env1.
>     env2 bind: #Griffle to: 2.
>     env2 bindingOf: #Griffle. "#Griffle=>2"
>     env1 bind: #Griffle to: 1.
>     env2 bindingOf: #Griffle. "#Griffle=>1"
> Note that the imported environment hides the local binding of env2.
> If the environments already come with bindings at "import time", the
> import order matters:
>     env1 := Environment new.
>     env2 := Environment new.
>     target := Environment new.
>     env1 exportSelf.
>     env2 exportSelf.
>     env1 bind: #Griffle to: 1.
>     env2 bind: #Griffle to: 2.
>     target import: env1; import: env2.
>     target bindingOf: #Griffle. "#Griffle=>2"
>     "...but when done the other way around (start over up to the imports)..."
>     target import: env2; import: env1.
>     target bindingOf: #Griffle. "#Griffle=>1"
> Now the funniest part: Let's assume target imported env1 last, so the
> binding comes from env1 and is #Griffle=>1. Guess what happens when
> env2 unbinds its #Griffle:
>     env2 unbind: #Griffle.
>     target bindingOf: #Griffle. "nil"
> So an environment can unbind something in another environment that was
> actually imported from a third environment. I consider this a bug and
> have a fix proposal for it, which I plan to put into the inbox soon.
> This behavior can have a really nasty consequence, which you can read
> up in the PS at the bottom if you like.
> Another peculiarity is that #importSelf does not overwrite existing
> bindings (if you imported something else before), but #import: and
> friends happily do overwrite bindings from self.
> What do you think about that reckless overwriting of bindings in
> general? I would like at least the "own" bindings (declarations) of an
> environment to have precedence over imported bindings. So you do not
> surprisingly lose your class binding when another class with the same
> name enters the original Smalltalk environment or is removed from it:
>     env := Environment withName: #Demo.
>     env importSelf; import: Smalltalk globals.
>     classfactory := ClassFactoryForTestCase new.
>     [class := classfactory newClass] on: CurrentEnvironment do: [:e |
> e resume: env].
>     (env valueOf: class name) environment. "Demo"
>     "put the CurrentEnvironment handling around the following instead
>      to test the removing case"
>     Object subclass: class name
>         instanceVariableNames: '' classVariableNames: ''
>         poolDictionaries: '' category: 'CategoryForTestToBeDeleted-Default'.
>     (env valueOf: class name) environment. "Smalltalk"
>     (Smalltalk at: class name) removeFromSystem.
>     env bindingOf: class name. "nil"
>     classfactory cleanUp.
> Kind regards,
> Jakob
> PS. Here the nasty story about the "bug" from above:
> It "literally destroys Smalltalk" when you attempt to #destroy a named
> environment (created with Environment withName: or named:) that
> imports "Smalltalk globals" and imports self. Every named environment
> has an own #Smalltalk declaration with an own SmalltalkImage instance
> that answers the named environment itself from "Smalltalk globals".
> Destroy unbinds all declarations, including this #Smalltalk. Because
> #importSelf does not overwrite existing bindings, the effective
> #Smalltalk binding is the one from the original Smalltalk environment,
> #Smalltalk=>Smalltalk (instead of
> #Smalltalk=>TheOtherEnvironmentsSmalltalk). Because the environment
> imports itself, it propagates the unbind: #Smalltalk to itself and
> eventually, as per the above bug, modifies the #Smalltalk=>Smalltalk
> binding with a nice becomeForward:. So the whole image has just lost
> its binding for #Smalltalk, nothing happens anymore, great.
> The code for the adventurous:
>     env := Environment withName: #Demo.
>     (env at: #Smalltalk) globals. "Demo"
>     env importSelf.
>     (env valueOf: #Smalltalk) globals. "Demo"
>     env import: Smalltalk globals.
>     (env valueOf: #Smalltalk) globals. "Smalltalk"
>     env destroy.

More information about the Squeak-dev mailing list