Author: ptw
Date: 2007-08-17 16:26:58 -0700 (Fri, 17 Aug 2007)
New Revision: 6134
Modified:
openlaszlo/branches/wafflecone/WEB-INF/lps/lfc/compiler/Class.lzs
openlaszlo/branches/wafflecone/WEB-INF/lps/lfc/core/LzDefs.lzs
openlaszlo/branches/wafflecone/WEB-INF/lps/server/sc/src/org/openlaszlo/sc/Parser.jjt
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/CodeGenerator.java
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/Compiler.java
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/JavascriptCompressor.java
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/JavascriptGenerator.java
Log:
Change 20070817-ptw-3 by [EMAIL PROTECTED] on 2007-08-17 07:12:53 EDT
in /Users/ptw/OpenLaszlo/wafflecone
for http://svn.openlaszlo.org/openlaszlo/branches/wafflecone
Summary: Remove `make` trampoline from Class system
Bugs Fixed:
LPP-1605 'ctor precedence is wrong'
LPP-4365 'Adding prototype functions to builtin objects a bit broken'
LPP-4513 'Compiler Improvements to address performance'
Technical Reviewer: max (Message-ID: <[EMAIL PROTECTED]>)
QA Reviewer: hminsky (pending)
Details:
LzDefs: Rework LzInheritedHash to not require .make.
Class: Rework contstructors to call .initialize, .make retained
for backwards-compatibility. Split out class validation code.
Compiler: Remove passThroughNodes before dispatching. Print new
expressions with correct associativity.
JavascriptGenerator, JavascriptCompressor, CodeGenerator: Remove
translation of new to .make
Parser.jjt: Learn how to correctly parse a `new` expressions and
maintain associativity.
Tests:
smokecheck in swf7, swf8, dhtml
Modified: openlaszlo/branches/wafflecone/WEB-INF/lps/lfc/compiler/Class.lzs
===================================================================
--- openlaszlo/branches/wafflecone/WEB-INF/lps/lfc/compiler/Class.lzs
2007-08-17 22:58:20 UTC (rev 6133)
+++ openlaszlo/branches/wafflecone/WEB-INF/lps/lfc/compiler/Class.lzs
2007-08-17 23:26:58 UTC (rev 6134)
@@ -20,8 +20,14 @@
*
* @access private
* @bootstrap true
+ *
+ * @devnote This is the first class constructor. Constructors for
+ * all other classes are created by Class.make (below).
*/
-var Instance = function () { this.constructor = arguments.callee };
+var Instance = function constructor () {
+ this.constructor = arguments.callee
+ this.initialize.apply(this, arguments);
+};
Instance.prototype.constructor = Instance;
Instance.prototype.__initialized = false;
Instance.classname = 'Instance';
@@ -71,7 +77,7 @@
// Delay 'til runtime so classes can adjust their name to be
// their tag name
value._dbg_owner = this;
- value._dbg_typename = function () {
+ value._dbg_typename = function _dbg_typename () {
var o = this._dbg_owner;
var t = o._dbg_typename;
// Prototypes have their own typename, instances should
@@ -139,6 +145,8 @@
* Default method does nothing
*
* @access private
+ *
+ * @devnote The call to the default method is optimized away in class
constructors
*/
Instance.prototype.addProperty('initialize', function initialize () {});
@@ -173,74 +181,89 @@
* Make a new instance of a class.
*
* @access private
+ *
+ * @devnote This is just for coherence with the Class interface.
+ * Normally new instances will be created by `new _class_`.
*/
Instance.make = function make () {
- // Scaffolding until the LFC is rewritten to use NewClass
- var prototype = this.prototype;
- do {
- var constructor = prototype.constructor;
- if (! prototype.__initialized) {
- // Debug.debug('Initializing', this.classname);
- for (var k in prototype) {
- if (prototype.hasOwnProperty(k) && k != 'constructor') {
- // Debug.debug(k);
- var value = prototype[k];
- // Cf., Instance.addProperty
- if (value instanceof Function) {
- if (value.hasOwnProperty('superclasses')) {
- var os = value.superclasses, found = false;
- for (var i = os.length - 1; i >= 0; i--) {
- if (os[i] === constructor) {
- found = true;
- break;
+ // Should not be called in the new world...
+ if ($debug) {
+ Debug.warn("`%w.%s` is deprecated. Use `new %w` instead.", this,
arguments.callee, this);
+ }
+ {
+ #pragma "passThrough=true"
+ if (arguments.length > 0) {
+ // We have to work hard to break a conventional constructor into a
+ // constructor and initializer when there are args, because
+ // Javascript does not support apply of new.
+ var constructor = this;
+ function xtor () { this.constructor = constructor; };
+ xtor.prototype = constructor.prototype;
+ var o = new xtor();
+ constructor.apply(o, arguments);
+ return o;
+ }
+ return new this();
+ }
+};
+
+if ($debug) {
+ Instance.make._dbg_typename = 'Instance static function';
+};
+
+if ($debug) {
+ Instance.prototype.addProperty('validateClassStructure', function
validateClassStructure () {
+ // Verifies that superclass links for nextMethod are correct
+ var prototype = this.constructor.prototype;
+ do {
+ var constructor = prototype.constructor;
+ if (! prototype.__initialized) {
+ // Debug.debug('Validating', constructor);
+ for (var k in prototype) {
+ if (prototype.hasOwnProperty(k) && k != 'constructor') {
+ // Debug.debug(k);
+ var value = prototype[k];
+ // Cf., Instance.addProperty
+ if (value instanceof Function) {
+ if (value.hasOwnProperty('superclasses')) {
+ var os = value.superclasses, found = false;
+ for (var i = os.length - 1; i >= 0; i--) {
+ if (os[i] === constructor) {
+ found = true;
+ break;
+ }
+ }
+ if (! found) {
+ value.superclasses.push(constructor);
+ Debug.error('Invalid class structure [3+]: Use `addProperty`
to create %w.%s', this, k);
+ }
+ } else if (value.hasOwnProperty('superclass')) {
+ var o = value.superclass;
+ if (o !== constructor) {
+ delete value.superclass;
+ value.superclasses = [ o, constructor ];
+ Debug.error('Invalid class structure [2]: Use `addProperty`
to create %w.%s', this, k);
+ }
+ } else {
+ value.superclass = constructor;
+ Debug.error('Invalid class structure [1]: Use `addProperty` to
create %w.%s', this, k);
}
}
- if (! found) {
- value.superclasses.push(constructor);
- if ($debug) {
- Debug.debug('Shim %s.%s [3+]', this, k)
- }
- }
- } else if (value.hasOwnProperty('superclass')) {
- var o = value.superclass;
- if (o !== constructor) {
- delete value.superclass;
- value.superclasses = [ o, constructor ];
- if ($debug) {
- Debug.debug('Shim %s.%s [2]', this, k);
- }
- }
- } else {
- value.superclass = constructor;
- if ($debug) {
- Debug.debug('Shim %s.%s [1]', this, k);
- }
}
}
+ prototype.__initialized = true;
}
- }
- // Debug.debug('[2] Initializing', this.classname);
- prototype.__initialized = true;
- }
- prototype = constructor.prototype;
- } while (constructor !== Instance);
+ prototype = constructor.prototype;
+ } while (constructor !== Instance);
+ });
+};
- #pragma "passThrough=true"
- var i = new this();
- i.initialize.apply(i, arguments);
- return i;
-}
-
-if ($debug) {
- Instance.make._dbg_typename = 'Instance static function';
-}
-
/**
* Bootstrap Class class
* @todo [2006-05-03 ptw] Rename to Class when LFC has been converted
*/
var Class = {
- prototype: Instance.make(),
+ prototype: new Instance(),
// Add a method to a class by adding it to the class's prototype
addProperty: function addProperty (name, value) {
var proto = this.prototype;
@@ -275,7 +298,7 @@
if ($debug) {
if (value instanceof Function && (! value._dbg_typename)) {
value._dbg_owner = this;
- value._dbg_typename = function () { return this._dbg_owner._dbg_name +
' static function'};
+ value._dbg_typename = function _dbg_typename () { return
this._dbg_owner._dbg_name + ' static function'};
}
}
},
@@ -289,7 +312,19 @@
// staticProperties:Object is a hash of the inital class properties;
make: function make (classname, traitsAndSuperclass, instanceProperties,
staticProperties) {
// The constructor notes itself in every instance
- var nc = function () { this.constructor = arguments.callee; };
+ var nc = function constructor () {
+ this.constructor = arguments.callee;
+ if ($debug) {
+ // Must come after the constructor is installed
+// Debug.debug("Validate", this, this.validateClassStructure);
+ this.validateClassStructure();
+ }
+ // Call the initializer if it is not the default
+ if (this.initialize !== Instance.prototype.initialize) {
+ // Debug.debug('Initializing', this);
+ this.initialize.apply(this, arguments);
+ }
+ };
nc.constructor = this;
nc.classname = classname;
if ($debug) {
@@ -328,8 +363,12 @@
// The prototype is an instance of our super, which causes us to
// inherit our super's instanceProperties
{
+ // This has to be constructed carefully, so as _not_ to run the
+ // class instance initializer
+ var xtor = function prototype () { this.constructor = superclass; };
+ xtor.prototype = superclass.prototype;
#pragma "passThrough=true"
- var prototype = new superclass; // --- superclass.make();
+ var prototype = new xtor(); // --- superclass.make();
}
// Create any trait interstitials, following the pattern above
if (traitsAndSuperclass instanceof Array) {
@@ -379,7 +418,7 @@
/** Bootstrap Trait class */
var Trait = {
- prototype: Instance.make(),
+ prototype: new Instance(),
allTraits: {},
_dbg_typename: Class._dbg_name,
_dbg_name: 'Trait',
@@ -394,7 +433,7 @@
t.addProperty.apply(t, arguments);
}
if ($debug) {
- if (value instanceof Function) {
+ if (value instanceof Function && (! value._dbg_typename)) {
value._dbg_typename = this._dbg_name + ' function';
}
}
@@ -402,7 +441,7 @@
addStaticProperty: function addStaticProperty (name, value) {
this[name] = value;
if ($debug) {
- if (value instanceof Function) {
+ if (value instanceof Function && (! value._dbg_typename)) {
value._dbg_typename = this._dbg_name + ' static function';
}
}
@@ -437,7 +476,7 @@
superclassInstance.addProperty.call(superclassInstance, name,
prototype[name]);
}
// Make the interstitial
- var xtor = function () { this.constructor = arguments.callee; };
+ var xtor = function interstitial () { this.constructor = arguments.callee;
};
xtor.prototype = superclassInstance;
// The trait's initialize method will run in Class.make (in the
// proper order).
@@ -449,8 +488,10 @@
// Unique name must identify superclass chain, punctuation is
// added for debugging
xtor.classname = mash;
- xtor._dbg_typename = 'Interstitial Constructor';
- xtor._dbg_name = xtor.classname;
+ if ($debug) {
+ xtor._dbg_typename = 'Interstitial Constructor';
+ xtor._dbg_name = xtor.classname;
+ }
#pragma "passThrough=true"
var t = new xtor();
// Remember
Modified: openlaszlo/branches/wafflecone/WEB-INF/lps/lfc/core/LzDefs.lzs
===================================================================
--- openlaszlo/branches/wafflecone/WEB-INF/lps/lfc/core/LzDefs.lzs
2007-08-17 22:58:20 UTC (rev 6133)
+++ openlaszlo/branches/wafflecone/WEB-INF/lps/lfc/core/LzDefs.lzs
2007-08-17 23:26:58 UTC (rev 6134)
@@ -69,18 +69,21 @@
* Used to efficiently clone hashtables using Object's
* N.B. these tables incorrectly will appear to have entries for all
* the properties of Object.prototype. To avoid this (but pay the
- * of a slower implementation, use LzDictionary).
+ * overhead of a slower implementation, use LzDictionary).
* @access private
*/
-var LzInheritedHash = {
- make: function make (parent) {
-#pragma "passThrough=true"
- if (parent) {
- function xtor() {};
+function LzInheritedHash (parent) {
+ var constructor = arguments.callee
+ if (! parent) {
+ this.constructor = constructor;
+ } else {
+ if ($as2) {
+ this.__proto__ = parent;
+ } else {
+ function xtor() { this.constructor = constructor; };
xtor.prototype = parent;
return new xtor();
}
- return new Object;
}
}
Modified:
openlaszlo/branches/wafflecone/WEB-INF/lps/server/sc/src/org/openlaszlo/sc/Parser.jjt
===================================================================
---
openlaszlo/branches/wafflecone/WEB-INF/lps/server/sc/src/org/openlaszlo/sc/Parser.jjt
2007-08-17 22:58:20 UTC (rev 6133)
+++
openlaszlo/branches/wafflecone/WEB-INF/lps/server/sc/src/org/openlaszlo/sc/Parser.jjt
2007-08-17 23:26:58 UTC (rev 6134)
@@ -384,12 +384,11 @@
void PrimarySuffix() #void : {}
{
- ((Arguments()) #FunctionCallParameters)
-| (("[" Expression() "]") #PropertyValueReference)
+ (("[" Expression() "]") #PropertyValueReference)
| (("." IdentifierOrKeyword()) #PropertyIdentifierReference)
}
-void Arguments() # void: {}
+void Arguments() #FunctionCallParameters: {}
{
"(" [ArgumentList()] ")"
}
@@ -418,9 +417,23 @@
(Identifier() | StringLiteral() | NumericLiteral()) ":"
AssignmentExpression()
}
+// A member expression is any number of '.' and '[' expressions
+void MemberExpression() #CallExpression(>1) : {}
+{
+ PrimaryExpression() ((LOOKAHEAD("." | "[") PrimarySuffix())*)
+}
+
+// A new expression ends with the first argument list
+void NewExpression() #NewExpression : {}
+{
+ "new" MemberExpression() ([LOOKAHEAD("(") Arguments()]
#EmptyExpression(jjtree.nodeArity()==0))
+}
+
+// Whereas a call expression allows intermingled '.', '[', and '(' expressions
void CallExpression() #CallExpression(>1) : {}
{
- PrimaryExpression() ((PrimarySuffix())*)
+ PrimaryExpression() (PrimarySuffix() | Arguments())*
+ | NewExpression() (PrimarySuffix() | Arguments())*
| SuperCallExpression()
}
@@ -430,17 +443,9 @@
([ LOOKAHEAD("." Identifier(), { "apply".equals(getToken(2).image)
|| "call".equals(getToken(2).image) })
"." Identifier()
] #EmptyExpression(jjtree.nodeArity()==0))
- (Arguments() #FunctionCallParameters)
+ Arguments()
}
-
-void LeftHandSideExpression() #NewExpression(>1) : {}
-{
-// LOOKAHEAD(3)
- (("new" CallExpression()) #NewExpression) | CallExpression()
-//| ("new" "super" (Arguments() #FunctionCallParameters))
-}
-
void PostfixOp() #Operator : {Token t;}
{
("++" | "--")
@@ -449,7 +454,7 @@
void PostfixExpression() #PostfixExpression(>1) : {}
{
- LeftHandSideExpression() [PostfixOp()]
+ CallExpression() [PostfixOp()]
}
void UnaryOp() #Operator : {Token t;}
Modified:
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/CodeGenerator.java
===================================================================
---
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/CodeGenerator.java
2007-08-17 22:58:20 UTC (rev 6133)
+++
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/CodeGenerator.java
2007-08-17 23:26:58 UTC (rev 6134)
@@ -2048,40 +2048,20 @@
public boolean visitNewExpression(SimpleNode node, boolean isReferenced,
SimpleNode[] children) {
SimpleNode ref = children[0];
- SimpleNode[] args = new SimpleNode[0];
- if (ref instanceof ASTCallExpression) {
- SimpleNode[] cn = ref.getChildren();
- ref = cn[0];
- args = cn[1].getChildren();
- }
- boolean primitive = false;
- if (ref instanceof ASTIdentifier) {
- String name = ((ASTIdentifier)ref).getName();
- primitive = "Object".equals(name) || "Array".equals(name) ||
"String".equals(name) ||
- "Boolean".equals(name) || "Number".equals(name) || "Date".equals(name)
||
- "Function".equals(name) || "RegExp".equals(name) ||
"Error".equals(name);
- }
- if (primitive || options.getBoolean("passThrough")) {
- visitCallParameters(node, isReferenced, args);
- boolean isref = translateReferenceForCall(ref, true, node);
- if (isref) {
- if (ref instanceof ASTPropertyIdentifierReference ||
- ref instanceof ASTPropertyValueReference) {
- collector.emit(Instructions.NewMethod);
- } else {
- collector.emit(Instructions.NEW);
- }
- } else {
- // This is how you invoke a function value
- collector.push(Values.Undefined);
+ SimpleNode[] args = children[1].getChildren();
+ visitCallParameters(node, isReferenced, args);
+ boolean isref = translateReferenceForCall(ref, true, node);
+ if (isref) {
+ if (ref instanceof ASTPropertyIdentifierReference ||
+ ref instanceof ASTPropertyValueReference) {
collector.emit(Instructions.NewMethod);
+ } else {
+ collector.emit(Instructions.NEW);
}
} else {
- Map map = new HashMap();
- map.put("_1", ref);
- map.put("_2", new Compiler.Splice(args));
- SimpleNode n = (new Compiler.Parser()).substitute("_1.make(_2)", map);
- visitCallExpression(n, isReferenced, n.getChildren());
+ // This is how you invoke a function value
+ collector.push(Values.Undefined);
+ collector.emit(Instructions.NewMethod);
}
return false;
}
Modified:
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/Compiler.java
===================================================================
---
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/Compiler.java
2007-08-17 22:58:20 UTC (rev 6133)
+++
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/Compiler.java
2007-08-17 23:26:58 UTC (rev 6134)
@@ -607,7 +607,7 @@
SimpleNode prop = node.get(1);
assert ((prop instanceof ASTPropertyIdentifierReference ||
prop instanceof ASTPropertyValueReference) &&
- prop.size() > 0 ): prop;
+ prop.size() > 0 ): (new
Compiler.ParseTreePrinter()).visit(prop);
int size = node.size();
SimpleNode children[] = new SimpleNode[2];
children[0] = node.get(0);
@@ -854,9 +854,14 @@
}
int size = node.size();
+ SimpleNode[] childnodes = node.getChildren();
String[] children = new String[size];
for (int i = 0; i < size; i++) {
- children[i] = visit(node.get(i)) ;
+ SimpleNode n = childnodes[i];
+ if (n instanceof PassThroughNode) {
+ n = childnodes[i] = ((PassThroughNode)n).realNode;
+ }
+ children[i] = visit(n) ;
}
Class nt = node.getClass();
@@ -1133,12 +1138,10 @@
return defaultVisitor(node, children);
}
public String visitNewExpression(SimpleNode node, String[] children) {
- // Associativity makes these parens superfluous
-// int thisPrec = prec(Ops.NEW, true);
-// SimpleNode c = node.get(0);
-// children[0] = maybeAddParens(thisPrec, c, children[0]);
- // Kludge for (new Foo).whatever
- return "(new " + children[0] + ")";
+ int thisPrec = prec(Ops.NEW, true);
+ SimpleNode c = node.get(0);
+ children[0] = maybeAddParens(thisPrec, c, children[0]);
+ return "new " + children[0] + "(" + children[1] + ")";
}
public String visitPragmaDirective(SimpleNode node, String[] children) {
return "#pragma " + children[0];
Modified:
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/JavascriptCompressor.java
===================================================================
---
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/JavascriptCompressor.java
2007-08-17 22:58:20 UTC (rev 6133)
+++
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/JavascriptCompressor.java
2007-08-17 23:26:58 UTC (rev 6134)
@@ -45,15 +45,6 @@
return node;
}
- // Don't transform new calls for compression
- public SimpleNode visitNewExpression(SimpleNode node, boolean isReferenced,
SimpleNode[] children) {
- for (int i = 0, len = children.length; i < len; i++) {
- SimpleNode child = children[i];
- children[i] = visitExpression(child, isReferenced);
- }
- return node;
- }
-
}
/**
Modified:
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/JavascriptGenerator.java
===================================================================
---
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/JavascriptGenerator.java
2007-08-17 22:58:20 UTC (rev 6133)
+++
openlaszlo/branches/wafflecone/WEB-INF/lps/server/src/org/openlaszlo/sc/JavascriptGenerator.java
2007-08-17 23:26:58 UTC (rev 6134)
@@ -1457,29 +1457,10 @@
}
public SimpleNode visitNewExpression(SimpleNode node, boolean isReferenced,
SimpleNode[] children) {
- SimpleNode ref = children[0];
- SimpleNode[] args = new SimpleNode[0];
- if (ref instanceof ASTCallExpression) {
- SimpleNode[] cn = ref.getChildren();
- ref = cn[0];
- args = cn[1].getChildren();
+ for (int i = 0, len = children.length; i < len; i++) {
+ SimpleNode child = children[i];
+ children[i] = visitExpression(child, isReferenced);
}
- boolean primitive = false;
- if (ref instanceof ASTIdentifier) {
- String name = ((ASTIdentifier)ref).getName();
- primitive = "Object".equals(name) || "Array".equals(name) ||
"String".equals(name) ||
- "Boolean".equals(name) || "Number".equals(name) || "Date".equals(name)
||
- "Function".equals(name) || "RegExp".equals(name) ||
"Error".equals(name);
- }
- if (primitive || options.getBoolean("passThrough")) {
- children[0] = visitExpression(children[0]);
- } else {
- Map map = new HashMap();
- map.put("_1", ref);
- map.put("_2", new Compiler.Splice(args));
- SimpleNode n = (new Compiler.Parser()).substitute("_1.make(_2)", map);
- node = visitExpression(n, isReferenced);
- }
return node;
}
_______________________________________________
Laszlo-checkins mailing list
[email protected]
http://www.openlaszlo.org/mailman/listinfo/laszlo-checkins