[Laszlo-checkins] r11902 - in openlaszlo/trunk/WEB-INF/lps: lfc lfc/compiler server/sc/src/org/openlaszlo/sc/parser server/src/org/openlaszlo/sc

dda@openlaszlo.org dda at openlaszlo.org
Wed Nov 26 10:42:57 PST 2008


Author: dda
Date: 2008-11-26 10:42:52 -0800 (Wed, 26 Nov 2008)
New Revision: 11902

Modified:
   openlaszlo/trunk/WEB-INF/lps/lfc/build.xml
   openlaszlo/trunk/WEB-INF/lps/lfc/buildlfc
   openlaszlo/trunk/WEB-INF/lps/lfc/buildlfcdebug
   openlaszlo/trunk/WEB-INF/lps/lfc/compiler/LzRuntime.lzs
   openlaszlo/trunk/WEB-INF/lps/server/sc/src/org/openlaszlo/sc/parser/ASTFormalParameterList.java
   openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Compiler.java
   openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/JavascriptGenerator.java
Log:
Change 20081121-dda-q by dda at lester.local on 2008-11-21 10:21:40 EST
    in /Users/dda/laszlo/src/svn/openlaszlo/trunk-g
    for http://svn.openlaszlo.org/openlaszlo/trunk

Summary: Add ability to insert try/catch blocks around functions

New Features: 

Bugs Fixed: LPP-7270 (swf9: debugger hanging)

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

Documentation:

Release Notes:

Details:
   This requested feature makes SWF9 similar like SWF8 and DHTML in the face of
   exceptions - in those systems, the exceptions are caught at the top level, and
   the program allowed to continue.  In our case, we catch the exceptions within
   every function body, report the error to the debug console, and return from the function.

.  To enable the feature, compile with -DcatchFunctionExceptions=true
   Some compilation problems were found in trying to use this feature with smokecheck.lzx
   for SWF9, so for the moment, this is not tied to the debug switch, but must be
   enabled explicitly.  This may be fixed soon.

   The basic premise is to change the ORIGINAL CONTENTS of a function to this:
       var $lzsc$ret:* = 0;
       try {
         $lzsc$ret = (function  () {
            with (this) {
               //ORIGINAL CONTENTS
         }}).call(this)
        }
        catch ($lzsc$e) {
           $lzsc$runtime.reportException("LPP-7270.lzx", 5, $lzsc$e)
        }
        finally {
           return $lzsc$ret
        }

   with the slight variation that if the original function is declared to return a type,
   then the closure is also typed e.g. function (): String { ...

   This construct has the property that it will compile almost any contents the same,
   preserving return values and reporting the same compiler errors, including paths
   that do not return a value.  For some reason, if the ORIGINAL CONTENTS contains a closure, 
   the flex compiler will report errors that attributes and even globals
   cannot be seen within that closure.  So this transformation is not done when
   the function contains a closure.  Also, super() calls cannot be pushed into a closure, so the
   transformation is not done if a super call is contained.

   A toString() function was added to ASTFormalParameterList to show the return type of the function.

   catchFunctionExceptions=false is added to the build scripts for the LFC.  This is technically
   not needed at the moment, but when we attach the catchFunctionExceptions option to the debug
   option, it will be.

Tests:
   Regression (swf8,swf9,dhtml) x (smokecheck,lzpix,weather)

   This change tested using the following test program, compiling with both
   -DcatchFunctionExceptions=true and false, observing generated compiler output
   for both cases, and running it with both cases.  Only the catchFunctionExceptions=true
   allows the program to proceed after an exception.

   test program: 
   <!-- ================================= -->
      <canvas debug="true">
        <simplelayout spacing="10" axis="y" />
           <class name="mywindow" width="300">
               <button onclick="this.parent.parent.canvas_f()" >BaseClass mywindow</button>
           </class>
       
         <method name="canvas_f">
          Debug.info("1 zebra");
          Debug.info("2");
          t1.setText("Before exception");
          Debug.info("3...?");
          throwexcept(7,0);
          Debug.info("4!!");
          t2.setText("After exception");
         </method>
         <button name="b1" onclick="this.parent.canvas_f()" width="100" height="30" y="10">Press me</button>
         <text name="t1" bgcolor="#ccffcc" width="100" height="30" y="50"/>
         <text name="t2" bgcolor="#ccffcc" width="100" height="30" y="100"/>
         <mywindow bgcolor="#cc8c8c"/>
       <script>
       function throwexcept(w, x) {
         // divide by zero doesn't give an exception, but
         // an array reference of a non array will.
         return w[x]/x;
       }

       </script>
     </canvas>
   <!-- ================================= -->




Modified: openlaszlo/trunk/WEB-INF/lps/lfc/build.xml
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/lfc/build.xml	2008-11-26 18:27:54 UTC (rev 11901)
+++ openlaszlo/trunk/WEB-INF/lps/lfc/build.xml	2008-11-26 18:42:52 UTC (rev 11902)
@@ -225,7 +225,7 @@
       var ext = (v == 9) ? ".swc" : ".lzl";
       var shlibopt = "--option buildSharedLibrary=true ";
       if (v == 9) {
-         shlibopt += "--option SWF9MainClassName=LFCApplication --option SWF9WrapperClassName=LzSpriteApplication ";
+         shlibopt += "--option SWF9MainClassName=LFCApplication --option SWF9WrapperClassName=LzSpriteApplication --option catchFunctionExceptions=false ";
       }
 
       // Check whether the options tell us to build this runtime at all

Modified: openlaszlo/trunk/WEB-INF/lps/lfc/buildlfc
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/lfc/buildlfc	2008-11-26 18:27:54 UTC (rev 11901)
+++ openlaszlo/trunk/WEB-INF/lps/lfc/buildlfc	2008-11-26 18:42:52 UTC (rev 11902)
@@ -17,8 +17,11 @@
     suffix="js"
 fi
 if [ "$runtime" == "swf9" -o "$runtime" == "swf10" ]; then
-    options="${options} --option buildSharedLibrary=true --option SWF9MainClassName=LFCApplication \
-     --option SWF9WrapperClassName=LzSpriteApplication"
+    options="${options} \
+     --option buildSharedLibrary=true \
+     --option SWF9MainClassName=LFCApplication \
+     --option SWF9WrapperClassName=LzSpriteApplication \
+     --option catchFunctionExceptions=false"
     suffix="swc"
 fi
 ant -Dlfc.output=${output:-LFC${runtime#swf}.${suffix:-"lzl"}} -Dlfc.runtime=${runtime} -Dlfc.source=LaszloLibrary.lzs \

Modified: openlaszlo/trunk/WEB-INF/lps/lfc/buildlfcdebug
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/lfc/buildlfcdebug	2008-11-26 18:27:54 UTC (rev 11901)
+++ openlaszlo/trunk/WEB-INF/lps/lfc/buildlfcdebug	2008-11-26 18:42:52 UTC (rev 11902)
@@ -23,8 +23,11 @@
     suffix="js"
 fi
 if [ "$runtime" == "swf9" -o "$runtime" == "swf10" ]; then
-    options="${options} -g --option buildSharedLibrary=true --option SWF9MainClassName=LFCApplication \
-    --option SWF9WrapperClassName=LzSpriteApplication"
+    options="${options} -g \
+     --option buildSharedLibrary=true \
+     --option SWF9MainClassName=LFCApplication \
+     --option SWF9WrapperClassName=LzSpriteApplication \
+     --option catchFunctionExceptions=false"
     suffix="swc"
 fi
 

Modified: openlaszlo/trunk/WEB-INF/lps/lfc/compiler/LzRuntime.lzs
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/lfc/compiler/LzRuntime.lzs	2008-11-26 18:27:54 UTC (rev 11901)
+++ openlaszlo/trunk/WEB-INF/lps/lfc/compiler/LzRuntime.lzs	2008-11-26 18:42:52 UTC (rev 11902)
@@ -75,6 +75,35 @@
 }
 }
 
+if ($as3) {
+  /**
+   * Class used for as3-only runtime support
+   *
+   * @access private
+   */
+  class $lzsc$runtime {
+
+  /**
+   * Report an exception to the debug window
+   *
+   * @param String fileName: The source file
+   * @param Number lineNumber: The source line
+   * @param Object e: exception received
+   *
+   * @access private
+   */
+    static function reportException (fileName, lineNumber, e) {
+        if ($debug) {
+          // TODO: [2008-11-26 dda] This message is not received?
+          //$reportSourceWarning(fileName, lineNumber, e.name + ": " + e.message, true);
+          Debug.warn("Exception ignored in function block at "
+                     + fileName + ":" + lineNumber
+                     + ": " + e.name + ": " + e.message);
+        }
+    }
+  }
+}
+
 if ($as2) {
 var encodeURIComponent = escape;
 }

Modified: openlaszlo/trunk/WEB-INF/lps/server/sc/src/org/openlaszlo/sc/parser/ASTFormalParameterList.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/sc/src/org/openlaszlo/sc/parser/ASTFormalParameterList.java	2008-11-26 18:27:54 UTC (rev 11901)
+++ openlaszlo/trunk/WEB-INF/lps/server/sc/src/org/openlaszlo/sc/parser/ASTFormalParameterList.java	2008-11-26 18:42:52 UTC (rev 11902)
@@ -33,4 +33,12 @@
         result.returnType = this.returnType;
         return result;
     }
+
+    public String toString() {
+        String s = super.toString();
+        if (this.returnType != null) {
+            s += "(returns:" + this.returnType.toString() + ")";
+        }
+        return s;
+    }
 }

Modified: openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Compiler.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Compiler.java	2008-11-26 18:27:54 UTC (rev 11901)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/Compiler.java	2008-11-26 18:42:52 UTC (rev 11902)
@@ -454,6 +454,7 @@
   public static String CONDITIONAL_COMPILATION = "conditionalCompilation";
   public static String ALLOW_ROOT = "allowRoot";
   public static String CACHE_COMPILES = "cacheCompiles";
+  public static String CATCH_FUNCTION_EXCEPTIONS = "catchFunctionExceptions";
   public static String COMPILE_TRACE = "compileTrace";
   public static String COMPILE_TIME_CONSTANTS = "compileTimeConstants";
   public static String CONSTRAINT_FUNCTION = "constraintFunction";

Modified: openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/JavascriptGenerator.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/JavascriptGenerator.java	2008-11-26 18:27:54 UTC (rev 11901)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/sc/JavascriptGenerator.java	2008-11-26 18:42:52 UTC (rev 11902)
@@ -1209,6 +1209,10 @@
         (new ParseTreePrinter()).print(depExpr);
       }
     }
+    // If either of error or suffix are set, a try block is created.
+    // predecls is only used if error/suffix are set, and are
+    // declarations that must appear before (outside of) try block
+    List predecls = new ArrayList();
     List prefix = new ArrayList();
     List error = new ArrayList();
     List suffix = new ArrayList();
@@ -1237,8 +1241,46 @@
       suffix.add((meterFunctionEvent(node, "returns", meterFunctionName)));
     }
 
+    // catchFunctionExceptions forces a catch of any exceptions.
+    // In debug mode the exception is reported, and in any case it is
+    // not propogated to our caller.
+    // To preserve return types, we implement this with a closure,
+    // but that introduces restrictions:
+    // We cannot put a super call in a closure.
+    // Having another function closure within a closure apparently
+    // causes problems accessing certain variables for the flex compiler.
+    boolean tryAll = options.getBoolean(Compiler.CATCH_FUNCTION_EXCEPTIONS)
+      && matchingAncestor(node.getParent(), ASTFunctionExpression.class) == null
+      && matchingDescendant(node, ASTSuperCallExpression.class) == null
+      && matchingDescendant(node, ASTFunctionExpression.class) == null
+      && functionName != null;
+      
+    String tryType = "";
+    if (tryAll) {
+      error.add(parseFragment("if ($debug) {" +
+                              "  $lzsc$runtime.reportException(" +
+                              ScriptCompiler.quote(functionNameIdentifier.filename) + ", " +
+                              functionNameIdentifier.beginLine + ", $lzsc$e); }"));
+
+      predecls.add(new Compiler.PassThroughNode(parseFragment("var $lzsc$ret:* = 0;")));
+      if (functionNameIdentifier != null && !functionNameIdentifier.isConstructor()) {
+        suffix.add(parseFragment("return $lzsc$ret;"));
+      }
+
+      // For typed functions, make the closure require a return,
+      // so the SWF9 compiler will be just as picky as without
+      // the closure.
+      if (((ASTFormalParameterList)params).getReturnType() != null) {
+        tryType = ": " + ((ASTFormalParameterList)params).getReturnType();
+      }
+      options.putBoolean(Compiler.CATCH_FUNCTION_EXCEPTIONS, false);
+    }
+
     // Analyze local variables (and functions)
     VariableAnalyzer analyzer = new VariableAnalyzer(params, options.getBoolean(Compiler.FLASH_COMPILER_COMPATABILITY));
+    for (Iterator i = predecls.iterator(); i.hasNext(); ) {
+      analyzer.visit((SimpleNode)i.next());
+    }
     for (Iterator i = prefix.iterator(); i.hasNext(); ) {
       analyzer.visit((SimpleNode)i.next());
     }
@@ -1437,6 +1479,13 @@
       SimpleNode newStmts = new ASTStatementList(0);
       newStmts.setChildren((SimpleNode[])newBody.toArray(new SimpleNode[0]));
       SimpleNode tryNode = new ASTTryStatement(0);
+      if (tryAll) {
+        Map map = new HashMap();
+        newStmts = visitStatement(newStmts);
+        map.put("_1", newStmts);
+        String frag = "$lzsc$ret = (function()" + tryType + " { with (this) { _1 }}).call(this);";
+        newStmts = new Compiler.PassThroughNode((new Compiler.Parser()).substitute(newStmts, frag, map));
+      }
       tryNode.set(i++, newStmts);
       if (! error.isEmpty()) {
         SimpleNode catchNode = new ASTCatchClause(0);
@@ -1455,6 +1504,7 @@
       }
       newBody = new ArrayList();
       newBody.add(tryNode);
+      newBody.addAll(0, predecls);
     }
     // Process amended body
     SimpleNode newStmts = new ASTStatementList(0);
@@ -1499,9 +1549,43 @@
     if (options.getBoolean(Compiler.CONSTRAINT_FUNCTION)) {
       return new SimpleNode[] { node, depExpr };
     }
+    if (tryAll) {
+      options.putBoolean(Compiler.CATCH_FUNCTION_EXCEPTIONS, true);
+    }
     return new SimpleNode[] { node, null };
   }
 
+  // walk up the AST and find a match by type
+  SimpleNode matchingAncestor(SimpleNode node, Class matchClass) {
+    while (node != null) {
+      if (matchClass.equals(node.getClass())) {
+        return node;
+      }
+      node = node.getParent();
+    }
+    return null;
+  }
+
+  // walk down the AST and find a match by type
+  SimpleNode matchingDescendant(SimpleNode node, Class matchClass) {
+    if (node == null) {
+      return null;
+    }
+    else if (matchClass.equals(node.getClass())) {
+      return node;
+    }
+    else {
+      SimpleNode[] children = node.getChildren();
+      for (int i=0; i<children.length; i++) {
+        SimpleNode result = matchingDescendant(children[i], matchClass);
+        if (result != null) {
+          return result;
+        }
+      }
+    }
+    return null;
+  }
+
   SimpleNode translateLiteralNode(SimpleNode node) {
     return node;
   }



More information about the Laszlo-checkins mailing list