Author: dda
Date: 2007-10-31 12:59:20 -0700 (Wed, 31 Oct 2007)
New Revision: 7066

Added:
   openlaszlo/trunk/test/trycatch.lzx
Modified:
   openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Actions.java
   openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Assembler.java
   openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/CodeGenerator.java
   
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/InstructionCollector.java
   openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Instructions.java
Log:
Change 20071029-dda-s by [EMAIL PROTECTED] on 2007-10-29 22:24:27 EDT
    in /Users/dda/laszlo/src/svn/openlaszlo/trunk
    for http://svn.openlaszlo.org/openlaszlo/trunk

Summary: support try/catch/finally and throw statements in SWF7/8

New Features: try/catch/finally supported in SWF7/8.

Bugs Fixed:  LPP-1539

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

Documentation:

Release Notes:
    try, catch, finally, throw statements are now supported in SWF7/8 as well 
as DHTML
    runtimes.

Details:
    try/catch/finally are very helpful in creating a reliable software.
    It should help in consolidating/maintaining code in the runtime kernel,
    since DHTML already supports try/catch and we won't need to have
    alternate implementations of error handling code.

Tests:
    created some simple tests using try/catch/finally,
    also try/catch (no finally).  Had some problems with
    my try/finally (no catch) example.
    The only way I've found to trigger an exception
    in SWF7/8 is to use an explicit throw statement,
    since the SWF instruction set seems to continue to operate
    with various kinds of errors.



Modified: openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Actions.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Actions.java      
2007-10-31 19:36:00 UTC (rev 7065)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Actions.java      
2007-10-31 19:59:20 UTC (rev 7066)
@@ -236,7 +236,7 @@
     public static Action EXTENDS                  = new Action("EXTENDS", 
(byte)0x69);
     public static Action DefineFunction2          = new 
Action("DefineFunction2", (byte)0x8e);
     public static Action TRY                      = new Action("TRY", 
(byte)0x8f);
-    public static Action THROW                    = new Action("THROW", 
(byte)0x2a);
+    public static Action THROW                    = new Action("THROW", 
(byte)0x2a, 1, 0);
 
     /*
      * Flash Lite 2

Modified: 
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Assembler.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Assembler.java    
2007-10-31 19:36:00 UTC (rev 7065)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Assembler.java    
2007-10-31 19:59:20 UTC (rev 7066)
@@ -43,6 +43,12 @@
   private static byte[] backingStore;
   Hashtable constants;
 
+  public static class LabelReference {
+    int patchloc;
+    boolean relative;
+    boolean positive;
+  }
+
   public static class Label {
     Object name;
     int location;
@@ -69,9 +75,26 @@
       location = bytes.position();
       // Backpatch forward jumps
       for (Iterator i =  references.iterator(); i.hasNext(); ) {
-        int patchloc = ((Integer)i.next()).intValue();
-        int offset = location - patchloc - 2;
-        if (offset < MIN_OFFSET() || offset > MAX_OFFSET()) {
+        LabelReference lr = (LabelReference)i.next();
+        int patchloc = lr.patchloc;
+        int offset = location - (lr.relative ? (patchloc + 2) : 0);
+        if (!lr.positive)
+          offset = -offset;
+
+        boolean rangecheck = true;
+        if (!lr.relative) {
+          short curval = bytes.getShort(patchloc);
+          offset += curval;
+
+          // When we're in the middle of evaluating an expression like
+          // (label1 - label0), the offset may be temporarily out of range,
+          // so disable the check
+
+          if (curval == 0)
+            rangecheck = false;
+        }
+
+        if (rangecheck && (offset < MIN_OFFSET() || offset > MAX_OFFSET())) {
           throw new CompilerException((this instanceof Block?"Block":"Label") 
+ " " +
                                       name + ": jump offset " + offset + " too 
large");
         }
@@ -92,8 +115,16 @@
     }
 
     public void addReference(int patchloc) {
+      addReference(patchloc, true, true);
+    }
+
+    public void addReference(int patchloc, boolean relative, boolean positive) 
{
       assert (! isResolved()) : "adding reference to resolved label";
-      references.add(new Integer(patchloc));
+      LabelReference lr = new LabelReference();
+      lr.patchloc = patchloc;
+      lr.relative = relative;
+      lr.positive = positive;
+      references.add(lr);
     }
 
     public String toString() {
@@ -170,6 +201,56 @@
     return (Label)labels.get(name);
   }
 
+  public void resolveTarget(TargetInstruction target, Label[] labels) {
+    if (labels.length > 1) {
+
+      // Multiple labels indicate that we are doing label arithmetic,
+      // each pair is a difference of values, used to compute the
+      // size of a code block.  This is needed for the try instruction.
+      // At the moment, we only implement this for forward references,
+      // which solves the only case we care about with the try instruction.
+
+      assert labels.length % 2 == 0 : "multiple target labels must be in 
pairs";
+
+      // Emit the instruction and then get the list of addresses
+      // to backpatch.
+
+      int origloc = bytes.position();
+      target.writeBytes(bytes, constants);
+
+      for (int i=0; i<labels.length; i+=2) {
+        Label labelpos = labels[i];
+        Label labelneg = labels[i+1];
+
+        assert (labelpos.location == -1 && labelneg.location == -1) :
+          "target arithmetic using backward refs not implemented";
+
+        int targetoff = target.targetOffset(i/2);
+        int patchloc = (targetoff < 0 ? bytes.position() : origloc) + 
targetoff;
+
+        labelpos.addReference(patchloc, false, true);  // add absolute pos 
value
+        labelneg.addReference(patchloc, false, false); // add absolute neg 
value
+      }
+    } else if (labels[0].location == -1) {
+      // Target location isn't yet available.  Use a null
+      // offset, and add the address to be patched to this
+      // label's list of backpatch locations.
+      int origloc = bytes.position();
+      target.writeBytes(bytes, constants);
+
+      int targetoff = target.targetOffset(0);
+      int patchloc = (targetoff < 0 ? bytes.position() : origloc) + targetoff;
+      labels[0].addReference(patchloc);
+    } else {
+      // Target computation requires that we write the instruction first!
+      target.targetOffset = 0;
+      target.writeBytes(bytes, constants);
+      short offset = labels[0].computeOffset(bytes);
+      assert bytes.order() == ByteOrder.LITTLE_ENDIAN;
+      bytes.putShort(bytes.position() - 2, offset);
+    }
+  }
+
   public void emit(Instruction instr) {
     // Verify there is room for a maximal instruction (1<<16)
     if (! (bytes.remaining() > 1<<16)) {
@@ -199,23 +280,19 @@
       label.setLocation(bytes);
     } else if (instr instanceof TargetInstruction) {
       TargetInstruction target = (TargetInstruction)instr;
-      Label label = getLabel(target.getTarget(), instr instanceof 
BranchInstruction);
-      int loc = label.location;
-      if (loc == -1) {
-        // Target location isn't yet available.  Use a null
-        // offset, and add the address to be patched to this
-        // label's list of backpatch locations.
-        target.writeBytes(bytes, constants);
-        int patchloc = bytes.position() - 2;
-        label.addReference(patchloc);
-      } else {
-        // Target computation requires that we write the instruction first!
-        target.targetOffset = 0;
-        target.writeBytes(bytes, constants);
-        short offset = label.computeOffset(bytes);
-        assert bytes.order() == ByteOrder.LITTLE_ENDIAN;
-        bytes.putShort(bytes.position() - 2, offset);
-        }
+      Object targetval = target.getTarget();
+      Label[] labels;
+      if (targetval instanceof Object[]) {
+        Object[] vals = (Object[])targetval;
+        labels = new Label[vals.length];
+        for (int i=0; i<vals.length; i++)
+          labels[i] = getLabel(vals[i], instr instanceof BranchInstruction);
+      }
+      else {
+        labels = new Label[1];
+        labels[0] = getLabel(targetval, instr instanceof BranchInstruction);
+      }
+      resolveTarget(target, labels);
     } else {
       instr.writeBytes(bytes, constants);
     }

Modified: 
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/CodeGenerator.java
===================================================================
--- 
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/CodeGenerator.java    
    2007-10-31 19:36:00 UTC (rev 7065)
+++ 
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/CodeGenerator.java    
    2007-10-31 19:59:20 UTC (rev 7066)
@@ -453,11 +453,95 @@
   }
 
   public SimpleNode visitTryStatement(SimpleNode node, SimpleNode[] children) {
-    throw new CompilerImplementationError("cannot handle try statement " + 
node, node);
+    SimpleNode block = children[0];
+    int len = children.length;
+    assert len == 2 || len == 3;
+    SimpleNode catchNode = null;
+    SimpleNode finallyNode = null;
+    int flags = 0;
+
+    if (len == 2) {
+      // Could be catch or finally clause
+      if (children[1] instanceof ASTCatchClause) {
+        catchNode = children[1];
+      }
+      else {
+        finallyNode = children[1];
+      }
+    }
+    else {
+      catchNode = children[1];
+      finallyNode = children[2];
+    }
+
+    String catchVarName = "";
+
+    // For catch and finally, reach down to get
+    // the target node to visit.
+    //
+    if (catchNode != null) {
+      SimpleNode[] catchchildren = catchNode.getChildren();
+      SimpleNode id = catchchildren[0];
+      assert id instanceof ASTIdentifier;
+      catchVarName = ((ASTIdentifier)id).getName();
+      catchNode = catchchildren[1];
+      flags |= Instructions.TryInstruction.FLAGS_HAS_CATCH;
+    }
+    if (finallyNode != null) {
+      finallyNode = finallyNode.getChildren()[0];
+      flags |= Instructions.TryInstruction.FLAGS_HAS_FINALLY;
+    }
+
+    // Try statement code looks like this in the general case:
+    //
+    //       try size0, size1, size2, 'varname'
+    //     label0:
+    //       ...try-block...
+    //       branch label2:
+    //     label1:
+    //       ...catch-block...
+    //     label2:
+    //       ...finally-block...
+    //     label3:
+    //
+    // where sizeN is the size of the code block between labelN and labelN+1.
+    // either catch or finally blocks may be missing.  Whether or not
+    // they are missing, we push out all the labels, it just makes it
+    // easier to calculate the block sizes.
+
+    ArrayList code = new ArrayList();
+
+    // The first six args are 3 pairs of labels to represent the size of
+    // each code block, e.g. label1 - label0 => size0
+    Object[] tryargs = { new Integer(1), new Integer(0), // label1 - label0
+                         new Integer(2), new Integer(1), // label2 - label1
+                         new Integer(3), new Integer(2), // label3 - label2
+                         catchVarName, new Integer(flags)};
+    code.add(Instructions.TRY.make(tryargs));
+    code.add(new Integer(0));
+    code.add(block);
+    if (catchNode != null)  // if catch is missing, no need for branch.
+      code.add(Instructions.BRANCH.make(2));
+    code.add(new Integer(1));
+    if (catchNode != null)
+      code.add(catchNode);
+    code.add(new Integer(2));
+    if (finallyNode != null)
+      code.add(finallyNode);
+    code.add(new Integer(3));
+
+    translateControlStructure(node, code.toArray());
+                       
+    return node;
   }
 
   public SimpleNode visitThrowStatement(SimpleNode node, SimpleNode[] 
children) {
-    throw new CompilerImplementationError("cannot handle throw statement " + 
node, node);
+    assert children.length == 1 : "throw statement missing expression";
+    SimpleNode expr = children[0];
+
+    visitExpression(expr);
+    collector.emit(Instructions.THROW);
+    return node;
   }
 
   SimpleNode translateInclude(String userfname, String cpass) {
@@ -902,15 +986,27 @@
       return label;
     }
 
+    String[] lookupLabels(Object[] vals) {
+      String[] rets = new String[vals.length];
+      for (int i=0; i<vals.length; i++) {
+        rets[i] = lookupLabel(vals[i]);
+      }
+      return rets;
+    }
+
     Instruction resolveLocalLabel(Object instr) {
       if (instr instanceof Integer) {
         return Instructions.LABEL.make(lookupLabel(instr));
       }
       if (instr instanceof Instructions.TargetInstruction) {
         Instructions.TargetInstruction target = 
(Instructions.TargetInstruction)instr;
-        if (target.getTarget() instanceof Integer) {
-          return target.replaceTarget(lookupLabel(target.getTarget()));
+        Object targetval = target.getTarget();
+        if (targetval instanceof Object[]) {
+          return target.replaceTarget(lookupLabels((Object[])targetval));
         }
+        if (targetval instanceof Integer) {
+          return target.replaceTarget(lookupLabel(targetval));
+        }
       }
       return (Instruction)instr;
     }

Modified: 
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/InstructionCollector.java
===================================================================
--- 
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/InstructionCollector.java
 2007-10-31 19:36:00 UTC (rev 7065)
+++ 
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/InstructionCollector.java
 2007-10-31 19:59:20 UTC (rev 7066)
@@ -28,7 +28,7 @@
  */
 
 /* J_LZ_COPYRIGHT_BEGIN *******************************************************
-* Copyright 2001-2004 Laszlo Systems, Inc.  All Rights Reserved.              *
+* Copyright 2001-2007 Laszlo Systems, Inc.  All Rights Reserved.              *
 * Use is subject to license terms.                                            *
 * J_LZ_COPYRIGHT_END *********************************************************/
 
@@ -93,6 +93,17 @@
     }
   }
 
+  // Rename labels uniquely
+  private Object uniqueLabel(Map labels, Object label)
+  {
+    Object newLabel = labels.get(label);
+    if (newLabel == null) {
+      newLabel = newLabel();
+      labels.put(label, newLabel);
+    }
+    return newLabel;
+  }
+
   public void appendInstructions(List instrsList) {
     // TODO [2003-03-06 ptw] Why not relabel all instructions? (I.e.,
     // move this to emit)
@@ -101,26 +112,12 @@
     for (int i = 0; i < instrs.length; i++) {
       Instruction instr = instrs[i];
       if (instr instanceof LABELInstruction) {
-        // Rename labels uniquely
-        Object label = ((LABELInstruction)instr).name;
-        Object newLabel;
-        if (labels.containsKey(label)) {
-          newLabel = labels.get(label);
-        } else {
-          newLabel = newLabel();
-          labels.put(label, newLabel);
-        }
+
+        Object newLabel = uniqueLabel(labels, ((LABELInstruction)instr).name);
         instr = Instructions.LABEL.make(newLabel);
       } else if (instr instanceof TargetInstruction) {
         TargetInstruction target = (TargetInstruction)instr;
-        Object label = target.getTarget();
-        Object newLabel;
-        if (labels.containsKey(label)) {
-          newLabel = labels.get(label);
-        } else {
-          newLabel = newLabel();
-          labels.put(label, newLabel);
-        }
+        Object newLabel = uniqueLabel(labels, target.getTarget());
         instr = target.replaceTarget(newLabel);
       }
       emit(instr);

Modified: 
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Instructions.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Instructions.java 
2007-10-31 19:36:00 UTC (rev 7065)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Instructions.java 
2007-10-31 19:59:20 UTC (rev 7066)
@@ -158,6 +158,8 @@
         inst = new DefineFunction2Instruction(op);
       } else if (op == Actions.WITH) {
         inst = new WITHInstruction(op);
+      } else if (op == Actions.TRY) {
+        inst = new TryInstruction(op);
       } else if (BranchInstruction.OPCODES.contains(op)) {
         inst = new BranchInstruction(op);
       } else {
@@ -492,6 +494,16 @@
     public int argsBytes() {
       return 2;
     }
+
+    // The offset of the target part of the instruction.
+    // If positive, it is relative from the beginning of the instruction,
+    // If negative, it is relative from the end.
+    // Default version has branch target as last two bytes
+
+    public int targetOffset(int whichTarget) {
+      return -2;
+    }
+
   }
 
 
@@ -545,7 +557,102 @@
     }
   }
 
+  public static class TryInstruction extends TargetInstruction {
 
+    // inherited targetOffset is ignored
+
+    short blockStart;
+    short catchStart;
+    short finallyStart;
+    short blockEnd;
+    public static int FLAGS_HAS_CATCH = 0x1;
+    public static int FLAGS_HAS_FINALLY = 0x2;
+
+    protected TryInstruction(Action op) {
+      this(op, null);
+    }
+    
+    protected TryInstruction(Action op, List args) {
+      this(op, args, (short)0, (short)0, (short)0, (short)0);
+    }
+
+    protected TryInstruction(Action op, List args, short blockStart, short 
catchStart, short finallyStart, short blockEnd) {
+      super(op, args);
+      assert op == Actions.TRY;
+      this.blockStart = blockStart;
+      this.catchStart = catchStart;
+      this.finallyStart = finallyStart;
+      this.blockEnd = blockEnd;
+    }
+
+    // The args are 6 labels, a variable name for the exception, and flags.
+    // The labels are three pairs - each pair represents label arithmetic,
+    // e.g. label1 - label0 to represent the size of a code block.
+
+    public TargetInstruction makeTargetInstruction(List args) {
+      return new TryInstruction(this.op, args);
+    }
+
+    // multiple target  by returning an array
+    public Object getTarget() {
+      Object[] result = new Object[6];
+      for (int i=0; i<6; i++)
+        result[i] = this.args.get(i);
+      return result;
+    }
+
+    public TargetInstruction replaceTarget(Object target) {
+      TargetInstruction replace = makeTargetInstruction(this.args);
+      Object[] targetarr = (Object[])target;
+      for (int i=0; i<6; i++)
+        replace.args.set(i, targetarr[i]);
+      return replace;
+    }
+
+    public void writeArgs(ByteBuffer bytes, Map pool) {
+      assert bytes.order() == ByteOrder.LITTLE_ENDIAN;
+      List args = this.args;
+      byte flags = (byte)((Integer)args.get(7)).intValue();
+      bytes.put(flags);
+      bytes.putShort(this.catchStart);
+      bytes.putShort(this.finallyStart);
+      bytes.putShort(this.blockEnd);
+      String varname = (String)args.get(6);
+      if (varname == null) varname = "";
+      try {
+        bytes.put(varname.getBytes("UTF-8"));
+      } catch (UnsupportedEncodingException e) {
+        assert false : "this can't happen";
+      }
+      bytes.put((byte)0);
+    }
+
+    public int argsBytes() {
+      List args = this.args;
+      String varname = (String)args.get(3);
+      if (varname == null) varname = "";
+      // size => 1 byte flag + three targets + string + null
+      int b = 1 + 6 + varname.length() + 1;
+      return b;
+    }
+
+    public int targetOffset(int whichTarget) {
+
+      // There are three targets to be filled,
+      // each is a two byte value, and they begin
+      // at byte 4 (after the flags)
+
+      return 4 + whichTarget*2;
+    }
+
+    public String toString() {
+      StringBuffer b = new StringBuffer();
+      b.append("try '" + (String)this.args.get(3) + "'");
+      return b.toString();
+    }
+
+  }
+
   public static class DefineFunctionInstruction extends TargetInstruction {
 
     protected DefineFunctionInstruction(Action op) {
@@ -1269,6 +1376,8 @@
 
   // Flash 7
   public static Instruction DefineFunction2          = 
Instruction.curry(Actions.DefineFunction2);
+  public static Instruction TRY                      = 
Instruction.curry(Actions.TRY);
+  public static Instruction THROW                    = 
Instruction.curry(Actions.THROW);
 
   // Flash Lite 2
   public static Instruction FSCommand2          = 
Instruction.curry(Actions.FSCommand2);

Added: openlaszlo/trunk/test/trycatch.lzx


Property changes on: openlaszlo/trunk/test/trycatch.lzx
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:eol-style
   + native


_______________________________________________
Laszlo-checkins mailing list
[email protected]
http://www.openlaszlo.org/mailman/listinfo/laszlo-checkins

Reply via email to