Look at following example:
It shows that become operation do not modifies p1 instance. It just replaces value in all references.
Now if p1 will be read only object then become will fail:
My question is why this logic is valid? As simple user I do not see how become modifies the state of receiver p1.
And what I expect is failing on another case which surprisingly do not fail:
Here become operation modifies state of read only object. But it not fails.
On Wed, Jan 10, 2018 at 2:46 AM, Denis Kudriashov <[hidden email]> wrote:
Well, the reason is to prevent changing literals. Let's say you have a method like
"Append to the argument, aStream, a sequence of characters that
identifies the receiver."
| title |
title := self class name.
nextPutAll: (title first isVowel ifTrue: ['an '] ifFalse: ['a ']);
Without read-only literals there is nothing to stop the programmer from making the mistake of doing something like
((Object>>#printOn:) literalAt: 4) at: 1 put: $A
which would cause Object new to print as 'An Object', not 'an Object'. Once literals are read-only then this can't happen; the at:put: will fail.
But one can use becomeForward: or become: in exactly the same way. Unless become is illegal for read-only objects, there is nothing to stop the programmer from making the mistake of doing((Object>>#printOn:) literalAt: 4) becomeForward: 'An '
((Object>>#printOn:) literalAt: 4) become: 'An '
The reason here is to do with instance migration on class definition. We want becomeForward: to update any and all instances of a class when we shape change a class. Further, the only way the VM can prevent this is by scanning the entire heap looking for any reference to the receiver of becomeForward: from a read-only object. And we want becomeForward: to be fast; the last thing we want is to introduce a full heap scan when we introduce read-only objects. So we're being pragmatic; the definitions here, that two-way become fails if either is a read-only object, and that one-way become fails if the receiver is read-only, work well with the way the system implements instance migration, and allow us to implement become in the presence of read-only objects without a full heap scan.
This matches the VW implementation which makes the same choices for the same reasons. It's not ideal, but neither is having the VM catch your last case. Instead it's a workable compromise. HTH
Likewise. Happy New Year!
Thank's for details.
2018-01-11 2:57 GMT+01:00 Eliot Miranda <[hidden email]>:
In reply to this post by Eliot Miranda-2
On Thu, Jan 11, 2018 at 2:57 AM, Eliot Miranda <[hidden email]> wrote:
I don't find this reason compelling. If the CompiledMethod itself was read-only, then yes, trying to change a literal in it should be an error, just like trying to change any slot in any read-only object.
Agreed. Literals should be read-only, they should never be modified.
No you can not. You can only replace the whole literal, but you can not change it.
True. But again, unless the CompiledMethod is read-only, I don't see a reason why this should fail any more than
(Object>>#printOn:) literalAt: 4
put: 'An '
I would expect this to fail, too.
Okay. If this is the semantics needed for a fast VM, that's a reason I can accept. It is just surprising that the semantics are both more restrictive (no become of a read-only object) and less restrictive (become can modify parts of a read-only object) than
Given this behavior of the VM, wouldn't it make sense to recursively make all sub-objects read-only when making an object read-only? That would ensure that parts cannot become'd either.
- Bert -
2018-01-13 20:04 GMT+01:00 Bert Freudenberg <[hidden email]>:
One scenario in st80 was:
^(self foo) nextPutAll: 'baz'; contents
But WriteStream was using become: to grow the target collection...
So the method foo was later pointing on a 'barbaz', and next execution of baz not in agreement with source code...
We can indeed make the CompiledMethod readOnly, but then, we have to scan the heap at each become:
So bye bye fast (lazy) become:...
Also beware, I think that our Squeak Environment rely on become: for shared variable bindings...
CompiledMethod directly points to these...
But we have so many cycles in the graph, if we touch a class (then an environment), the whole graph is frozen...
On 13.01.2018, at 21:26, Nicolas Cellier <[hidden email]> wrote:
But i see it's a becomeForward: not become: so it would fall in exceptional cases described by Eliot for mutating instances...
I mean Environment operations like bind:to: or undeclare:from:
Hi Tobias,I don't mean changing the value of the binding.
> On 13.01.2018, at 23:03, Nicolas Cellier <[hidden email]> wrote:
> Hi Tobias,
> I don't mean changing the value of the binding.
> I mean Environment operations like bind:to: or undeclare:from:
> But i see it's a becomeForward: not become: so it would fall in exceptional cases described by Eliot for mutating instances...
Yeah, I thought that bindings (or associations then) were a device to make #become: less painful on things that tangentially touch CompiledMethods..
> 2018-01-13 21:38 GMT+01:00 Tobias Pape <[hidden email]>:
>> On 13.01.2018, at 21:26, Nicolas Cellier <[hidden email]> wrote:
>> CompiledMethod directly points to these...
> Do they?
> <Bildschirmfoto 2018-01-13 um 21.36.33.PNG>
|Free forum by Nabble||Edit this page|