[Laszlo-dev] For Review: Change 20070921-dda-l Summary: Fix script compiler switch stack mixup in default label

Donald Anderson dda at ddanderson.com
Fri Sep 21 15:31:20 PDT 2007


Change 20070921-dda-l by dda at freddie.local on 2007-09-21 17:52:38 EDT
     in /Users/dda/laszlo/src/svn/openlaszlo/trunk
     for http://svn.openlaszlo.org/openlaszlo/trunk

Summary:  Fix script compiler switch stack mixup in default label

New Features:

Bugs Fixed: LPP-3023

Technical Reviewer: ptw
QA Reviewer: (pending)
Doc Reviewer: (pending)

Documentation:

Release Notes:

Details:
    The problem is in the script compiler, specifically for the SW7/8  
code generator.
    When creating a jump table (a series of comparisons to jump to  
the case labels),
    each jump action leaves a value at the top of the stack that must  
be popped as the
    first action of the case label.  However, default was handled  
differently.
    The extra value was popped first and the jump to the default  
label is made,
    so the default label did not pop as its first action.

    Here's the problem.  When case labels are joined with the default  
label like this:
        case 10:
        case 11:
        default:
           // do something...
    the code generator attempts to share the code, but the stack  
levels are mismatched.
    One solution is to force the default label to be the last and  
insert the extra pop
    for the case labels only:
        case 10:
        case 11:
           pop stack
           // fall through
        default:

    I opted for a simpler solution, to guarantee that stack levels  
are always the same.
    During the jump table, we avoid popping the stack before the  
default jump.  We must
    take care that if there is no default label, the stack is popped  
before the jump table
    completes and we jump to the end of the switch code block.
    With this approach, we now always emit a pop as the first action  
of a default
    label, like every other case label.  This allows case labels to  
be easily
    joined whether or not they represent the default case.

    I don't believe there is any performance impact: we are doing the  
same number of pops
    at runtime (well, one extra that is needed to fix the buggy  
situation), and the
    overall code size should be the same.

Tests:
   Ran test listed in LPP-3023 and made sure it is now correct, both  
with debug and no debug.
   Enhanced the app to test a number of other corner cases for  
default labels and verified
   the output, again with/without debug.  App included here:

  <canvas>
   <class name="switcher" extends="text">
     <method name="testSwitch" args="val">
       var result = "none";
       switch (val) {
         case 1:
           result = "case 1";
           break;
         case 2:
         case 3:
           // if you uncomment the true below both 2 and 3 work
           // true;
         default:
           // if you remove the default: above 2 and 3 also work
           result = "default";
           break;
       }
       this.setText(this.name + ": " + result);
     </method>
     <method name="testSwitchDefaultBreak" args="val">
       var result = "none";
       switch (val) {
         case 1:
           result = "case 1";
           break;
         case 2:
         case 3:
           // if you uncomment the true below both 2 and 3 work
           // true;
         default:
           break;
       }
       this.setText(this.name + ": " + result);
     </method>
     <method name="testSwitchDefaultNoBreak" args="val">
       var result = "none";
       switch (val) {
         case 1:
           result = "case 1";
           break;
         case 2:
         case 3:
           // if you uncomment the true below both 2 and 3 work
           // true;
         default:
       }
       this.setText(this.name + ": " + result);
     </method>
     <method name="testSwitchNoDefault" args="val">
       var result = "none";
       switch (val) {
         case 1:
           result = "case 1";
           break;
         case 2:
         case 3:
           // if you uncomment the true below both 2 and 3 work
           // true;
       }
       this.setText(this.name + ": " + result);
     </method>
   </class>
   <simplelayout />
   <switcher name="switch1" />
   <switcher name="switch2" />
   <switcher name="switch3" />
   <switcher name="switch4" />
   <switcher name="switch11" />
   <switcher name="switch21" />
   <switcher name="switch31" />
   <switcher name="switch41" />
   <switcher name="switch12" />
   <switcher name="switch22" />
   <switcher name="switch32" />
   <switcher name="switch42" />
   <switcher name="switch13" />
   <switcher name="switch23" />
   <switcher name="switch33" />
   <switcher name="switch43" />
   <text name="switch5">
     <method name="otherSwitch" args="val">
       var result = "none";
       switch (val) {
         case 1:
           result = "case 1";
           break;
         case 2:
         case 3:
           // if you uncomment the true below both 2 and 3 work
           // true;
         default:
           // if you remove the default: above 2 and 3 also work
           result = "default";
           break;
       }
       this.setText(this.name + ": " + result);
     </method>
   </text>
   <text name="switch6">
     <method name="nonSwitch" args="val">
       var result = "none";
       if (val == 1) {
           result = "case 1";
        } else {
           result = "default";
       }
       this.setText(this.name + ": " + result);
     </method>
   </text>
   <text name="switch7">
   </text>
   <script>
     // the first case works
     switch1.testSwitch(1);
     // the default case works;
     switch2.testSwitch(5);
     // using 2 or 3 appears to work but breaks all method calls
     switch3.testSwitch(2);
     // none of these locally defined calls work
     switch4.testSwitch(1);

     switch11.testSwitchDefaultBreak(1);
     switch21.testSwitchDefaultBreak(5);
     switch31.testSwitchDefaultBreak(2);
     switch41.testSwitchDefaultBreak(1);

     switch12.testSwitchDefaultNoBreak(1);
     switch22.testSwitchDefaultNoBreak(5);
     switch32.testSwitchDefaultNoBreak(2);
     switch42.testSwitchDefaultNoBreak(1);

     switch13.testSwitchNoDefault(1);
     switch23.testSwitchNoDefault(5);
     switch33.testSwitchNoDefault(2);
     switch43.testSwitchNoDefault(1);

     switch5.otherSwitch(1);
     switch6.nonSwitch(1);
     switch7.setText(this.name + ": " + "test");

   </script>
  </canvas>

Files:
M      WEB-INF/lps/server/src/org/openlaszlo/sc/CodeGenerator.java

Changeset: http://svn.openlaszlo.org/openlaszlo/patches/20070921-dda- 
l.tar



--

Don Anderson
Java/C/C++, Berkeley DB, systems consultant

Voice:  617-547-7881
Email:  dda at ddanderson.com
WWW:    http://www.ddanderson.com



More information about the Laszlo-dev mailing list