Author: dda
Date: 2008-03-10 19:22:40 -0700 (Mon, 10 Mar 2008)
New Revision: 8218
Modified:
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/SWF9Generator.java
Log:
Change 20080310-dda-1 by [EMAIL PROTECTED] on 2008-03-10 09:47:18 EDT
in /Users/dda/laszlo/src/svn/openlaszlo/branches/devildog
for http://svn.openlaszlo.org/openlaszlo/branches/devildog
Summary: SWF9: Create constructors for interstitial classes
New Features:
Bugs Fixed:
Technical Reviewer: ptw (pending)
QA Reviewer: promanik (pending)
Doc Reviewer: (pending)
Documentation:
Release Notes:
Details:
The use of mixins cause 'interstitial' classes to be created to
simulate the mixin. These classes sit in the inheritence tree
between the class that uses the mixin and the 'real' super class.
If the super class has a constructor with >0 args, and the mixin
does not declare a constructor, then there will be compilation errors.
This change automatically creates a constructor with the right
number of args, etc. when there is not one in the mixin.
If a mixin declares a constructor, it is obligated to call super
with a compatible arg list for any super class it might be used with.
Also this change fixes a blemish with multiple mixins.
class C1 with M1,M2 extends S1
used to create an interstitial named 'lzsc$mixin$M1$lzsc$mixin$M2$S1',
now it creates the more compact, and originally intended name:
'lzsc$mixin$M1$M2$S1'.
Tests:
Regression: henry's hello (SWF9) + smokecheck(SWF8,DHTML)
Functionality: Temporarily inserted a sample set of mixins, superclasses
and classes
that use them into LzNode.js and made sure generated code was correct and
compiled.
Modified:
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/SWF9Generator.java
===================================================================
---
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/SWF9Generator.java
2008-03-11 02:12:16 UTC (rev 8217)
+++
openlaszlo/branches/devildog/WEB-INF/lps/server/src/org/openlaszlo/sc/SWF9Generator.java
2008-03-11 02:22:40 UTC (rev 8218)
@@ -72,6 +72,11 @@
Map programVars = new HashMap();
/**
+ * Any classes are put here after translation.
+ */
+ Map classes = new HashMap();
+
+ /**
* The 'default' class is implicit and encompasses everything
* not declared within an explicit 'class' definition. This variable
* is set when we are outside of explicit classes.
@@ -91,10 +96,12 @@
/** values for the mixinRef map */
class MixinReference {
String mixinname;
- String supername;
- MixinReference(String m, String s) {
- mixinname = m;
- supername = s;
+ String supername; // name of immediate super, may be interstitial
+ String realsuper; // name of closest non-interstitial super
+ MixinReference(String m, String s, String r) {
+ this.mixinname = m;
+ this.supername = s;
+ this.realsuper = r;
}
}
@@ -190,12 +197,13 @@
// create reference to mixin classes
int nmixins = mixins.size();
String superiname = supername; // iname == 'interstitial name'
+ String suffix = (supername == null) ? "" : supername;
+ String realsuper = supername;
for (int i=nmixins-1; i>=0; i--) {
String mixinname = ((ASTIdentifier)mixins.get(i)).getName();
- String iname = "$lzsc$mixin$" + mixinname + "$";
- if (superiname != null)
- iname += superiname;
- mixinRef.put(iname, new MixinReference(mixinname, superiname));
+ suffix = mixinname + "$" + suffix;
+ String iname = "$lzsc$mixin$" + suffix;
+ mixinRef.put(iname, new MixinReference(mixinname, superiname,
realsuper));
superiname = iname;
}
children[2] = newIdentifier(superiname);
@@ -226,6 +234,8 @@
mixinDef.put(classnameString, node);
node = mixinInterface; // the interface will appear in the final tree
translateClassDefinition(node, classnameString,
TranslateHow.AS_INTERFACE);
+ } else {
+ classes.put(classnameString, node);
}
}
finally {
@@ -346,7 +356,17 @@
if (mixin == null) {
throw new CompilerError("Missing definition for mixin: " +
ref.mixinname);
}
- result.set(result.size(), createInterstitial(mixin, isname,
ref.mixinname, ref.supername));
+ // We need the actual super class because we need
+ // a trampoline to any constructor that it has.
+ SimpleNode superClass = null;
+
+ if (ref.realsuper != null) {
+ superClass = (SimpleNode)classes.get(ref.realsuper);
+ if (superClass == null) {
+ throw new CompilerError("Superclass " + ref.realsuper + " not
defined for mixin: " + ref.mixinname);
+ }
+ }
+ result.set(result.size(), createInterstitial(mixin, isname, ref,
superClass));
}
return result;
}
@@ -361,19 +381,133 @@
return id;
}
- SimpleNode createInterstitial(SimpleNode mixin, String isname, String
mixinname, String superisname)
+ /**
+ * For a class or mixin node, return the constructor
+ * if there is one. Throw an error if more than one
+ * is found.
+ * @param node class node, if null no constructor returned
+ * @return a constructor, or null.
+ */
+ SimpleNode getConstructor(SimpleNode node) {
+ SimpleNode result = null;
+ if (node != null) {
+ SimpleNode[] dirs = node.getChildren();
+ for (int i = 0; i < dirs.length; i++) {
+ SimpleNode n = dirs[i];
+
+ if (n instanceof ASTModifiedDefinition) {
+ n = n.get(0);
+ }
+ if (n instanceof ASTFunctionDeclaration) {
+ ASTIdentifier fid = (ASTIdentifier)n.get(0);
+ if (fid.isConstructor()) {
+ if (result != null) {
+ throw new CompilerError(fid.getName() + ": multiple constructors
not allowed");
+ }
+ result = n;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Build a constructor function that is a 'trampoline' to its
+ * superclass.
+ * @param name name of new constructor function
+ * @param formalList the formal arguments from super class
+ * @param actuals the actual arguments for super(...)
+ * @return AST for the new constructor function
+ */
+ SimpleNode buildTrampoline(String name, SimpleNode formalList, SimpleNode[]
actuals) {
+ // Build the super statement
+ SimpleNode call = new ASTFunctionCallParameters(0);
+ call.setChildren(actuals);
+ SimpleNode expr = new ASTSuperCallExpression(0);
+ SimpleNode[] exprch = {new ASTEmptyExpression(0),new
ASTEmptyExpression(0),call};
+ expr.setChildren(exprch);
+ SimpleNode stmt = new ASTStatement(0);
+ SimpleNode[] stmtch = {expr};
+ stmt.setChildren(stmtch);
+ SimpleNode[] mirchildren = new SimpleNode[3];
+ mirchildren[0] = newIdentifier(name);
+ mirchildren[1] = formalList; // sharing formals in the AST
+ mirchildren[2] = new ASTStatementList(0);
+ SimpleNode[] listch = {stmt};
+ mirchildren[2].setChildren(listch);
+ SimpleNode tramp = new ASTFunctionDeclaration(0);
+ tramp.setChildren(mirchildren);
+ SimpleNode f = new ASTModifiedDefinition(0);
+ SimpleNode[] fch = {tramp};
+ f.setChildren(fch);
+ return f;
+ }
+
+ /**
+ * Create a constructor for an interstitial class.
+ * It has the same signature as the super and
+ * calls super() with the right number of args. If the super constructor
+ * has optional arguments e.g. function MyClass(x, y = 3),
+ * we'll declare signature the same and call super with the full set,
+ * e.g. MyInterstitial(x, y = 3) { super(x,y); }
+ * This works no matter how long the interstitial chain is.
+ * @param newname name of new constructor
+ * @param supernode AST of 'real' superclass to match args
+ * @return AST for constructor
+ */
+ SimpleNode createInterstitialConstructor(String newname, SimpleNode
supernode) {
+ SimpleNode constructor = getConstructor(supernode);
+ SimpleNode result;
+ if (constructor == null) {
+ // make default constructor
+ result = buildTrampoline(newname, new ASTFormalParameterList(0), new
SimpleNode[0]);
+ }
+ else {
+ SimpleNode[] origparams = constructor.get(1).getChildren();
+
+ // actual arguments for the super(arg1, arg2, ...) call
+ List actuals = new ArrayList();
+ for (int i=0; i<origparams.length; i++) {
+ if (origparams[i] instanceof ASTIdentifier) {
+ ASTIdentifier id = (ASTIdentifier)origparams[i];
+ if (id.getEllipsis()) {
+ // Somewhat difficult to handle this case, and unknown
+ // if it's worth the effort.
+ throw new CompilerError(newname + ": cannot have variable args in
constructor used as parent class of mixin");
+ }
+ actuals.add(id); // sharing the identifier
+ }
+ }
+ SimpleNode[] actualsArray = (SimpleNode[])actuals.toArray(new
SimpleNode[0]);
+ result = buildTrampoline(newname, constructor.get(1), actualsArray);
+ }
+ return result;
+ }
+
+ /**
+ * Create an interstitial class.
+ * @param mixin mixin that this interstitial implements
+ * @param isname name for the interstitial
+ * @param mixref the reference that caused this class to be created
+ * @param supernode the 'real' superclass, or null if none
+ * @return the new interstitial class AST
+ */
+ SimpleNode createInterstitial(SimpleNode mixin, String isname,
MixinReference mixref, SimpleNode supernode)
{
// At this point, the mixin has already been visited.
// We just need to grab its contents and make a fresh class
- // out of it.
-
+ // out of it, adding in a trampoline for the superclass constructor.
+ //
SimpleNode[] mixinchildren = mixin.getChildren();
- SimpleNode[] ischildren = new SimpleNode[mixinchildren.length];
+ int origlen = mixinchildren.length;
+ SimpleNode mixinconstructor = getConstructor(mixin);
+ SimpleNode[] ischildren = new SimpleNode[origlen + (mixinconstructor ==
null ? 1 : 0)];
SimpleNode isnode = new ASTClassDefinition(0);
ischildren[0] = newIdentifier("class");
ischildren[1] = newIdentifier(isname);
- ischildren[2] = newIdentifier(superisname);
+ ischildren[2] = newIdentifier(mixref.supername);
// Before the tree is visited, child[3] has a list of
// 'with'/'inherits' - that is, names of mixins.
@@ -382,8 +516,12 @@
// something that the class 'implements'.
// For an interstitial, that is the name of the mixin.
- ischildren[3] = newIdentifier(mixinname);
- System.arraycopy(mixinchildren, 4, ischildren, 4, mixinchildren.length -
4);
+ ischildren[3] = newIdentifier(mixref.mixinname);
+ System.arraycopy(mixinchildren, 4, ischildren, 4, origlen - 4);
+ if (mixinconstructor == null) {
+ ischildren[origlen] = createInterstitialConstructor(isname, supernode);
+ }
+
isnode.setChildren(ischildren);
return isnode;
_______________________________________________
Laszlo-checkins mailing list
[email protected]
http://www.openlaszlo.org/mailman/listinfo/laszlo-checkins