[squeak-dev] Cuis problem with blocks

Eliot Miranda eliot.miranda at gmail.com
Thu Jan 21 18:04:47 UTC 2010


On Thu, Jan 21, 2010 at 4:33 AM, Juan Vuletich <juan at jvuletich.org> wrote:

> Thanks, Nicolas. You're right. I did include the preference in Cuis. By
> default it is set not to allow assignment to block args. I recompiled the
> whole system this way, removing any block arg assgnment left. I see no
> reason to allow them, but the preference is there if someone needs it.
>

Agreed.  I put in the preference for cases such as loading packages
containing assignments to block temps.  Without the preference it is
additional work to load the package (unpacking the .mcz by hand and editing
the source file).  Once the package is loaded one can hunt down and rewrite
those assignments.  But how does one find them other than by resetting the
preference and recompiling the package?  And how does one find block
argument assignments lurking in unpackaged code save recompiling the entire
system?  One abuses MessageNotUnderstood, that's how.  Here's a hack
implementation of SystemNavigation allBlockArgumentAssignations.  It has
just found 35 such abominations in my development image (shock, horror).
 Change set attached.  Code in all its syntax coloured glory below.  Enjoy.

*SystemNavigation methods for query*
allBlockArgumentAssignations
        "Answer all methods containing an assignment to a block argument."
        "SystemNavigation default
                 browseMessageList: SystemNavigation default
allBlockArgumentAssignations asSortedCollection
                 name: 'Assignments to block arguments'"
        ^self allMethodsSelect:
                 [:m| | d assignsToBlockArg scanner |
                 assignsToBlockArg *:=* false.
                 (d *:=* m blockpcsToBlockExtents) size >= 2 ifTrue:
                          [scanner *:=* InstructionStream on: m.
                          d keysDo:
                                   [:interval| | nArgs |
                                   (interval first ~= m initialPC
                                    and: [(nArgs *:=* m
argumentCountForBlockAtPC: interval first) > 0]) ifTrue:
                                            [scanner pc: interval first.
                                            [scanner pc <= interval last]
whileTrue:
                                                     [[scanner
interpretNextInstructionFor: nil]
                                                              on:
 MessageNotUnderstood
                                                              do: [:ex|
                                                                       ((#(
popIntoTemporaryVariable:

        storeIntoTemporaryVariable:) includes: ex message selector)
                                                                       and:
[ex message arguments first + 1 <= nArgs]) ifTrue:

        [assignsToBlockArg *:=* true]]]]]].
                          assignsToBlockArg]

*CompiledMethod methods for debugger support*
argumentCountForBlockAtPC: blockStartPC
        "Answer the argument count of the block whose first bytecode is
startpc."
        (self pcPreviousTo: blockStartPC) ifNotNil:
                 [:closureCreationPC|
                 [(InstructionStream on: self)
                          pc: closureCreationPC;
                          interpretNextInstructionFor: nil]
                          on: MessageNotUnderstood
                          do: [:ex|
                                   ex message selector ==
#pushClosureCopyNumCopiedValues:numArgs:blockSize: ifTrue:
                                            [^ex message arguments at: 2]]
].
        self error: 'pc is not a block start pc'

blockpcsToBlockExtents
        "Answer a Dictionary of (Interval from startpc to lastpc) to
(Interval of blockExtent),
         for the method and any blocks within it, using the identical
numbering scheme described
         in and orchestrated by
BlockNode>>analyseArguments:temporaries:rootNode:.
         This is a variation on startpcsToBlockExtents whcih only answers
startpc to interval."
        | index |
        index *:=* 0.
        ^self
                 blockRangesAndExtentsInto: Dictionary new
                 from: self initialPC
                 to: self endPC
                 scanner: (InstructionStream on: self)
                 numberer: [| value | value *:=* index. index *:=* index + 2
. value]

blockRangesAndExtentsInto: aDictionary from: initialPC to: endPC scanner:
scanner numberer: numbererBlock
        "Support routine for blockpcsToBlockExtents"
        | extentStart blockSizeOrLocator |
        self flag: 'belongs in DebuggerMethodMap'.
        extentStart *:=* numbererBlock value.
        [scanner pc <= endPC] whileTrue:
                 [blockSizeOrLocator *:=* scanner
interpretNextInstructionFor: BlockStartLocator new.
                  blockSizeOrLocator isInteger ifTrue:
                          [self
                                   blockRangesAndExtentsInto: aDictionary
                                   from: scanner pc
                                   to: scanner pc + blockSizeOrLocator - 1
                                   scanner: scanner
                                   numberer: numbererBlock]].
        aDictionary at: (initialPC to: endPC) put: (extentStart to:
numbererBlock value).
        ^aDictionary

p.s. its my birthday today.  I should be doing something useful but this was
too much fun, my present to myself.


> Cheers,
> Juan Vuletich
>
>
> Nicolas Cellier wrote:
>
>> Writing to block arguments is considered bad practice.
>> In most Smalltalk (all?), you cannot write method arguments.
>> Since blocks are proper closures (introduced in Cuis 2.0) same rule
>> now apply to block arguments.
>> In squeak trunk, this behavior is driven by a Preferences, you can ask
>> Juan if he applied same policy in Cuis.
>>
>> Anyway, [:sum :ea | sum := sum + ea] is completely useless since the
>> variable sum cease to exist once the block closure finish execution.
>> It has exactly same effect as [:sum :ea | sum + ea].
>>
>> This pattern was the result of some bad understanding of block return
>> value and particularly the way inject:into: works, even in the
>> pre-closure world.
>>
>> Cheers
>>
>> Nicolas
>>
>> 2010/1/21 Phil (list) <pbpublist at gmail.com>:
>>
>>
>>> I'm attempting to get some code running on Cuis 2.0 and am having a
>>> problem
>>> with some pretty simple blocks like:
>>>
>>> [:sum :ea | sum := sum + ea] value: 1 value: 2.
>>>
>>> resulting in an error:
>>>
>>> [:sum :ea |  Cannot store into ->sum := sum + ea] value: 1 value: 2.
>>>
>>> However, this example works without a problem in both 3.10.2 as well as
>>> 3.7.
>>>  Any idea what's going on?
>>>
>>> Thanks,
>>> Phil
>>>
>>>
>>>
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20100121/b4955bcd/attachment.htm


More information about the Squeak-dev mailing list