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