"Create a foreign environment importing all from Smalltalk globals" foreign := Environment withName: #Foreign. foreign exportSelf. foreign import: Smalltalk globals. "Bind a new global" fooKey := #ClassVarScopeFoo. fooValue := Smalltalk globals at: fooKey put: Object basicNew. fooBinding := Smalltalk globals bindingOf: fooKey. self assert: (foreign bindingOf: fooKey) == fooBinding. "Unbind the global: it is moved to, and shared by both undeclared" Smalltalk globals removeKey: fooKey. self assert: (Smalltalk globals undeclared associationAt: fooKey) == fooBinding. self assert: (foreign undeclared associationAt: fooKey) == fooBinding. "Create a class var: the undeclared binding is moved to classPool. This is questionable, it was a global with potentially larger/different scope." parent := Object subclass: #ClassVarScopeParent instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Dummy-Tests-Class'. child := parent subclass: #ClassVarScopeChild instanceVariableNames: '' classVariableNames: 'ClassVarScopeFoo' poolDictionaries: '' category: 'Dummy-Tests-Class'. self assert: (child classPool associationAt: fooKey) == fooBinding. "The global is no more in Smalltalk globals undeclared" self assert: (Smalltalk globals undeclared includesKey: fooKey) not. "But it is still in foreign undeclared" self assert: (foreign undeclared associationAt: fooKey) == fooBinding. "Rebind a new global" fooValue := Smalltalk globals at: fooKey put: Object basicNew. globalFooBinding := Smalltalk globals bindingOf: fooKey. "The binding has been removed from foreign undeclared" self assert: (foreign undeclared includesKey: fooKey) not. self assert: (foreign bindingOf: fooKey) == globalFooBinding. "But because #showBinding: did use a becomeForward: the class pool and global bindings are now surprisingly fused. That explains that a foreign environment importing Smalltalk globals is enough to make ClassVarScopeTest fail" self assert: globalFooBinding == fooBinding. self assert: (child classPool associationAt: fooKey) == globalFooBinding. "save our souls" classes := { child. parent }. child := parent := nil. classes do: [ :each | each removeFromChanges; removeFromSystemUnlogged ]. classes := nil. Smalltalk globals removeKey: fooKey. foreign destroy. |
Some lessons from this snippet: - the undeclared shall better point weakly to the bindings, letting them being garbaged collected if possible - what do we expect when we have two unrelated bindings #Foo->nil in envA undeclared and envB undeclared, then import envA into envB (envB import: envA)? I would expect (envB undeclared associationAt: #Foo) becomeForward: (envA undeclared associationAt: #Foo)... - moving a binding from undeclared to some classPool/sharedPool (PoolDictionary) is completely ignoring the variable scope by now, which may lead to erroneous re-binding.I don't see obvious solution for the last point (testing that all references to the binding are reachable by the defining class scope thru methodDictionary and subclasses methodDictionary?). For PoolDictionary, it's even more tough (going thru all classes sharing this poolDictionary). I didn't see any attempt at handling the case when both environment A & B declare a variable Foo, and environment C imports both A and B... 2016-10-25 10:30 GMT+02:00 Nicolas Cellier <[hidden email]>:
|
Free forum by Nabble | Edit this page |