[squeak-dev] renaming a class causes invalid super pointer CompiledMethod..!

Tobias Pape Das.Linux at gmx.de
Tue Jan 20 20:35:23 UTC 2015


On 20.01.2015, at 21:33, Eliot Miranda <eliot.miranda at gmail.com> wrote:

> On Tue, Jan 20, 2015 at 12:26 PM, Eliot Miranda <eliot.miranda at gmail.com> wrote:
> Hi Chris, Hi Colin,
> 
> 
> On Thu, Jan 15, 2015 at 2:48 PM, Chris Muller <ma.chris.m at gmail.com> wrote:
> I did a minor class-hierarchy refactoring today which resulted in a an
> invalid super pointer, resulting in a confusing image lock up due to
> tight recursion.  The script, below, demonstrates the issue in trunk:
> 
> Object compile: 'test  ^''Hello from Object'''.
> (Object subclass: #MyClass instanceVariableNames: ''
> classVariableNames: '' poolDictionaries: '' category: 'Test') compile:
> 'test  ^ super halt test, ''my super call lands on myself!'''.
> (Smalltalk classNamed: #MyClass) rename: 'MyNewAbstraction'.
> ((Smalltalk classNamed: #MyNewAbstraction) subclass: #MyClass
> instanceVariableNames: '' classVariableNames: '' poolDictionaries: ''
> category: 'Test').
> (Smalltalk classNamed: #MyNewAbstraction) new test
> 
> But an existing method in the original MyClass had a call to super
> which now lands in itself, resulting in a tight recursion.
> 
> The same script without creating the new subclass results in an
> instant VM crash (both interpreter 2357 and cog 3205):
> 
> Object compile: 'test  ^''Hello from Object'''.
> (Object subclass: #MyClass instanceVariableNames: ''
> classVariableNames: '' poolDictionaries: '' category: 'Test') compile:
> 'test  ^ super halt test, ''my super call lands on itself!'''.
> (Smalltalk classNamed: #MyClass) rename: 'MyNewAbstraction'.
> (Smalltalk classNamed: #MyNewAbstraction) new test
> 
> I even tried recompiling my entire class hierarchy which was renamed,
> but it didn't help.
> 
> How can this be?  I _KNOW_ I've used rename class many times in the
> past...  A regression?
> 
> This is indeed an Environments regression.  The issue is where to fix the problem.  One way to look at it is that Environment>>renameClass:from:to: does not update bindings in the renamed class's methods.
> 
> Here's what the old code did:
> 
> SystemDictionary>>renameClass: aClass from: oldName to: newName
> 	"Rename the class, aClass, to have the title newName."
> 
> 	| oldref category |
> 	category := SystemOrganization categoryOfElement: oldName.
> 	self organization classify: newName under: category suppressIfDefault: true.
> 	self organization removeElement: oldName.
> 	oldref := self associationAt: oldName.
> 	self removeKey: oldName.
> **	oldref key: newName.
> 	self add: oldref.  "Old association preserves old refs"
> 	Smalltalk renamedClass: aClass from: oldName to: newName.
> 	self flushClassNameCache.
> 	SystemChangeNotifier uniqueInstance classRenamed: aClass from: oldName to: newName inCategory: category 
> 
> The starred line changes the key on the old class's binding, meaning that all the methods whose methodClassAssociation is that binding get automatically updated (note that class methods have a methodClassAssociation that has no key and just points to the class object).
> 
> The new code simply forgets to do this:
> 
> Environment>>renameClass: aClass from: oldName to: newName
> 	"Rename the class, aClass, to have the title newName."
> 
> 	| binding category |
> 	category := self organization categoryOfElement: oldName.
> 	self organization classify: newName under: category suppressIfDefault: true.
> 	self organization removeElement: oldName.
> 	
> 	binding := self declarationOf: oldName.
> 	declarations removeKey: oldName.
> 	self binding: binding removedFrom: self.
> 	
> 	binding := newName => aClass.
> 	declarations add: binding. 
> 	self binding: binding addedTo: self.
> 	
> 	Smalltalk renamedClass: aClass from: oldName to: newName.
> 	SystemChangeNotifier uniqueInstance 
> 		classRenamed: aClass 
> 		from: oldName 
> 		to: newName 
> 		inCategory: category
> 
> 
> But should the change go in Environment>>renameClass:from:to: or in the BindingPolicy?  i.e. it could be implemented either in Environment>>renameClass:from:to:, or in Environment>>showBinding:.  I guess the former, but I'm cc'ing Colin, because know it?, he wrote it!
> 
> and if the fix should indeed go in Environment>>renameClass:from:to: , it is as simple as
> 
> Environment>>renameClass: aClass from: oldName to: newName
> 	"Rename the class, aClass, to have the title newName."
> 
> 	| binding category |
> 	category := self organization categoryOfElement: oldName.
> 	self organization classify: newName under: category suppressIfDefault: true.
> 	self organization removeElement: oldName.
> 	
> 	binding := self declarationOf: oldName.
> 	declarations removeKey: oldName.
> 	self binding: binding removedFrom: self.
> 	
> 	binding := newName => aClass.
> 	declarations add: binding. 
> 	self binding: binding addedTo: self.
> 
> **	aClass updateMethodBindingsTo: binding.
> 	
> 	Smalltalk renamedClass: aClass from: oldName to: newName.
> 	SystemChangeNotifier uniqueInstance 
> 		classRenamed: aClass 
> 		from: oldName 
> 		to: newName 
> 		inCategory: category
> 
> I'll make this change and we can alter later if Colin thinks it's in the wrong place.
>  
> 



Eliot, be sure to check my changes before you do that.
We try to fix the same problem!

Best
	-Tobias




> In any case there needs to be a test that checks that a method's methodClassAssociation is correctly updated after a rename.  I bet Colin would have not made this slip had there been a test.
> 
> 
> Here's the code I used to test this:
> 
> | mca1 mca2 |
> Object compile: 'test  ^''Hello from Object'''.
> (Object
> 	subclass: #MyClass instanceVariableNames: ''
> 	classVariableNames: ''
> 	poolDictionaries: ''
> 	category: 'Test')
> 		compile: 'test  ^ super halt test, ''my super call lands on itself!'''.
> mca1 := ((Smalltalk classNamed: #MyClass)>>#test) methodClassAssociation.
> (Smalltalk classNamed: #MyClass)
> 	rename: 'MyNewAbstraction'.
> mca2 := ((Smalltalk classNamed: #MyNewAbstraction)>>#test) methodClassAssociation.
> { mca1. mca2. mca1 == mca2. mca1 == (Smalltalk bindingOf: #MyNewAbstraction). mca2 == (Smalltalk bindingOf: #MyNewAbstraction)}
> 
> It answers { #MyClass=>nil . #MyClass=>nil . true . false . false}
> i.e. the binding isn't updated, but changes from #MyClass=>MyClass to #MyClass=>nil in binding:removedFrom:.





More information about the Squeak-dev mailing list