<div dir="ltr">Hi All,<div><br></div><div>   from Chris's requirement #4, <span style="color:rgb(0,0,0)">     4)  Ability to ask any Preference its default value, without resetting its current value,</span> here's some simple code for extracting the default value from a pragma preference method, if it specifies a literal default value.</div><div class="gmail_extra"><br>The three styles of providing defaults in pragma preferences are essentially</div><div class="gmail_extra"><br></div><div class="gmail_extra">    ^ClassVar ifNil: [#someDefaultLiteral]</div><div class="gmail_extra"><br></div><div class="gmail_extra">    ^ClassVar ifNil: [ClassVar := #someDefaultLiteral]</div><div class="gmail_extra"><br></div><div class="gmail_extra">    anything else (and we'll see all of these soon).</div><div class="gmail_extra"><br></div><div class="gmail_extra">Looking at the byte codes for the first two forms we see </div><div class="gmail_extra"><br></div><div class="gmail_extra"><div class="gmail_extra"><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>21 <40> pushLit: ThoroughSenders</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>22 <88> dup</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>23 <73> pushConstant: nil</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">  </span>24 <C6> send: ==</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>25 <99> jumpFalse: 28</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>26 <87> pop</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>27 <71> pushConstant: true</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre"> </span>28 <7C> returnTop</div><div class="gmail_extra"><br></div><div class="gmail_extra">and</div><div class="gmail_extra"><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">  </span>21 <40> pushLit: ShowSugarNavigator</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>22 <88> dup</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>23 <73> pushConstant: nil</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">  </span>24 <C6> send: ==</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>25 <9B> jumpFalse: 30</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>26 <87> pop</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>27 <72> pushConstant: false</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>28 <81 C0> storeIntoLit: ShowSugarNavigator</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>30 <7C> returnTop</div><div class="gmail_extra"><br></div><div class="gmail_extra">CompiledMethod provides abstractBytecodeMessagesDo: to enumerate over the byte codes independently of the byte code set's encoding.  So this extracts the default value from one of the two above:</div><div class="gmail_extra"><br></div><div class="gmail_extra"><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>| literal |<br></div><div class="gmail_extra"><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">   </span>self abstractBytecodeMessagesDo:</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">         </span>[:msg|</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">           </span>msg selector == #pushConstant:</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">                   </span>ifTrue: [literal := msg argument]</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">                        </span>ifFalse:</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">                         </span>[msg selector == #methodReturnTop ifTrue: [^literal].</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">                            </span> msg selector ~~ #storeIntoLiteralVariable: ifTrue:</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">                                      </span>[literal := nil]]].</div><div class="gmail_extra"><span class="gmail-Apple-tab-span" style="white-space:pre">      </span>literal</div><div><br></div></div><div class="gmail_extra">It works by identifying any literal via "msg selector == #pushConstant:" and stores it in literal.  Then if it sees anything other than #methodReturnTop or #storeIntoLiteralVariable: it nils the literal.  And it returns the value of literal as soon as it sees #methodReturnTop.</div><div class="gmail_extra"><br></div><div class="gmail_extra">So the following browses all the pragma preferences in the system that don't fit the first two pattens:</div><div class="gmail_extra"><br></div></div></div></div><div><div><span class="gmail-Apple-tab-span" style="white-space:pre">        </span>self systemNavigation browseAllSelect:</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">         </span>[:m|</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">           </span> (m pragmaAt: #preference:category:description:type:) notNil</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">           </span> and: [| literal |</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                     </span>m abstractBytecodeMessagesDo:</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                          </span>[:msg|</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                         </span>msg selector == #pushConstant:</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                 </span>ifTrue: [literal := msg argument]</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                      </span>ifFalse:</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                               </span>[(msg selector ~~ #methodReturnTop</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                             </span>  and: [msg selector ~~ #storeIntoLiteralVariable:]) ifTrue:</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                                                  </span>[literal := nil]]].</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                            </span>literal isNil]]</div></div><div><br></div><div>Of 121 pragma preference methods in my system 22 don't fit the pattern.  Some are project-specific preferences that defer to Project's own custom preference framework, which is necessary to allow individual projects to have their individual preferences (something that the dictionary of preferences framework also cannot handle without special accommodation).  Some are bad examples:</div><div><br></div><div><div><span class="gmail-Apple-tab-span" style="white-space:pre">       </span>booleanPref</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">            </span><preference: 'Boolean Preference Example'</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                   </span>category: 'Examples'</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                   </span>description: 'A simple example for a boolean preference  (see PreferenceExample>>booleanPref)'</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                  </span>type: #Boolean></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">             </span>^BooleanPref</div></div><div><br></div><div>Some are richer:</div><div><div><span class="gmail-Apple-tab-span" style="white-space:pre">    </span>activeColor</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">            </span><preference: 'Corner Grip highlight color'</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                  </span>category: 'colors'</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                     </span>description: 'The highlight-color of window corners'</div><div><span class="gmail-Apple-tab-span" style="white-space:pre">                   </span>type: #Color></div><div><span class="gmail-Apple-tab-span" style="white-space:pre">               </span>^(self activeForm colorAt: 24@24) alpha:  1</div></div><div><br></div><div>But I hope you get the point that with very little machinery we can extract the default value in a substantial majority of cases.</div><div><br></div><div>Since we'd also like to get rid of the setter it would be good to be able to extract the class variable so it can be set simply by sending it #value: , so... allow me to add abstractBytecodeMessages, then something similar to the following (*)</div><div><br></div><div><span style="white-space:pre">     (</span>((method abstractBytecodeMessages first: 5) collect: [:ea| ea selector]) hasEqualElements: #(#pushLiteralVariable: #doDup #pushConstant: #send:super:numArgs: #jump:if:)) ifTrue:<br></div><div><span style="white-space:pre">  </span><span style="white-space:pre">     [^(method abstractBytecodeMessageAt: method initialPC) argument]</span><br></div><div><span style="white-space:pre"><br></span></div><div><span style="white-space:pre">answers the class variable, allowing the Preference UI to</span></div><div><span style="white-space:pre">- derive its current value</span></div><div><span style="white-space:pre">- reset its value given the default extracted above</span></div><div><span style="white-space:pre"><br></span></div><div><span style="white-space:pre">(*) </span>one could insist on the send being of #== and the pushConstant: supplying nil.</div><div><br></div><div><span style="white-space:pre">So with these two pieces of machinery in the Preferences UI we don't need setter methods for pragma preferences at all.  Note that none of the bytecode magic has to be visible to the author of a pragma preference, but i does have to be understood by the maintainer of the Preference UI.</span></div><div><br></div><div>HTH</div><div class="gmail_signature"><div dir="ltr"><div><span style="font-size:small;border-collapse:separate"><div>_,,,^..^,,,_<br></div><div>best, Eliot</div></span></div></div></div>
</div></div>