001: /* ***** BEGIN LICENSE BLOCK *****
002: * Version: MPL 1.1/GPL 2.0
003: *
004: * The contents of this file are subject to the Mozilla Public License Version
005: * 1.1 (the "License"); you may not use this file except in compliance with
006: * the License. You may obtain a copy of the License at
007: * http://www.mozilla.org/MPL/
008: *
009: * Software distributed under the License is distributed on an "AS IS" basis,
010: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
011: * for the specific language governing rights and limitations under the
012: * License.
013: *
014: * The Original Code is Rhino code, released
015: * May 6, 1999.
016: *
017: * The Initial Developer of the Original Code is
018: * Netscape Communications Corporation.
019: * Portions created by the Initial Developer are Copyright (C) 1997-1999
020: * the Initial Developer. All Rights Reserved.
021: *
022: * Contributor(s):
023: * Norris Boyd
024: * Roger Lawrence
025: *
026: * Alternatively, the contents of this file may be used under the terms of
027: * the GNU General Public License Version 2 or later (the "GPL"), in which
028: * case the provisions of the GPL are applicable instead of those above. If
029: * you wish to allow use of your version of this file only under the terms of
030: * the GPL and not to allow others to use your version of this file under the
031: * MPL, indicate your decision by deleting the provisions above and replacing
032: * them with the notice and other provisions required by the GPL. If you do
033: * not delete the provisions above, a recipient may use your version of this
034: * file under either the MPL or the GPL.
035: *
036: * ***** END LICENSE BLOCK ***** */
037:
038: package org.mozilla.javascript.optimizer;
039:
040: import org.mozilla.javascript.*;
041:
042: class Optimizer {
043:
044: static final int NoType = 0;
045: static final int NumberType = 1;
046: static final int AnyType = 3;
047:
048: // It is assumed that (NumberType | AnyType) == AnyType
049:
050: void optimize(ScriptOrFnNode scriptOrFn) {
051: // run on one function at a time for now
052: int functionCount = scriptOrFn.getFunctionCount();
053: for (int i = 0; i != functionCount; ++i) {
054: OptFunctionNode f = OptFunctionNode.get(scriptOrFn, i);
055: optimizeFunction(f);
056: }
057: }
058:
059: private void optimizeFunction(OptFunctionNode theFunction) {
060: if (theFunction.fnode.requiresActivation())
061: return;
062:
063: inDirectCallFunction = theFunction.isTargetOfDirectCall();
064: this .theFunction = theFunction;
065:
066: ObjArray statementsArray = new ObjArray();
067: buildStatementList_r(theFunction.fnode, statementsArray);
068: Node[] theStatementNodes = new Node[statementsArray.size()];
069: statementsArray.toArray(theStatementNodes);
070:
071: Block.runFlowAnalyzes(theFunction, theStatementNodes);
072:
073: if (!theFunction.fnode.requiresActivation()) {
074: /*
075: * Now that we know which local vars are in fact always
076: * Numbers, we re-write the tree to take advantage of
077: * that. Any arithmetic or assignment op involving just
078: * Number typed vars is marked so that the codegen will
079: * generate non-object code.
080: */
081: parameterUsedInNumberContext = false;
082: for (int i = 0; i < theStatementNodes.length; i++) {
083: rewriteForNumberVariables(theStatementNodes[i]);
084: }
085: theFunction
086: .setParameterNumberContext(parameterUsedInNumberContext);
087: }
088:
089: }
090:
091: /*
092: Each directCall parameter is passed as a pair of values - an object
093: and a double. The value passed depends on the type of value available at
094: the call site. If a double is available, the object in java/lang/Void.TYPE
095: is passed as the object value, and if an object value is available, then
096: 0.0 is passed as the double value.
097:
098: The receiving routine always tests the object value before proceeding.
099: If the parameter is being accessed in a 'Number Context' then the code
100: sequence is :
101: if ("parameter_objectValue" == java/lang/Void.TYPE)
102: ...fine..., use the parameter_doubleValue
103: else
104: toNumber(parameter_objectValue)
105:
106: and if the parameter is being referenced in an Object context, the code is
107: if ("parameter_objectValue" == java/lang/Void.TYPE)
108: new Double(parameter_doubleValue)
109: else
110: ...fine..., use the parameter_objectValue
111:
112: If the receiving code never uses the doubleValue, it is converted on
113: entry to a Double instead.
114: */
115:
116: /*
117: We're referencing a node in a Number context (i.e. we'd prefer it
118: was a double value). If the node is a parameter in a directCall
119: function, mark it as being referenced in this context.
120: */
121: private void markDCPNumberContext(Node n) {
122: if (inDirectCallFunction && n.getType() == Token.GETVAR) {
123: int varIndex = theFunction.getVarIndex(n);
124: if (theFunction.isParameter(varIndex)) {
125: parameterUsedInNumberContext = true;
126: }
127: }
128: }
129:
130: private boolean convertParameter(Node n) {
131: if (inDirectCallFunction && n.getType() == Token.GETVAR) {
132: int varIndex = theFunction.getVarIndex(n);
133: if (theFunction.isParameter(varIndex)) {
134: n.removeProp(Node.ISNUMBER_PROP);
135: return true;
136: }
137: }
138: return false;
139: }
140:
141: private int rewriteForNumberVariables(Node n) {
142: switch (n.getType()) {
143: case Token.EXPR_VOID: {
144: Node child = n.getFirstChild();
145: int type = rewriteForNumberVariables(child);
146: if (type == NumberType)
147: n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
148: return NoType;
149: }
150: case Token.NUMBER:
151: n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
152: return NumberType;
153:
154: case Token.GETVAR: {
155: int varIndex = theFunction.getVarIndex(n);
156: if (inDirectCallFunction
157: && theFunction.isParameter(varIndex)) {
158: n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
159: return NumberType;
160: } else if (theFunction.isNumberVar(varIndex)) {
161: n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
162: return NumberType;
163: }
164: return NoType;
165: }
166:
167: case Token.INC:
168: case Token.DEC: {
169: Node child = n.getFirstChild();
170: // "child" will be GETVAR or GETPROP or GETELEM
171: if (child.getType() == Token.GETVAR) {
172: if (rewriteForNumberVariables(child) == NumberType) {
173: n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
174: markDCPNumberContext(child);
175: return NumberType;
176: }
177: return NoType;
178: } else if (child.getType() == Token.GETELEM) {
179: return rewriteForNumberVariables(child);
180: }
181: return NoType;
182: }
183: case Token.SETVAR: {
184: Node lChild = n.getFirstChild();
185: Node rChild = lChild.getNext();
186: int rType = rewriteForNumberVariables(rChild);
187: int varIndex = theFunction.getVarIndex(n);
188: if (inDirectCallFunction
189: && theFunction.isParameter(varIndex)) {
190: if (rType == NumberType) {
191: if (!convertParameter(rChild)) {
192: n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
193: return NumberType;
194: }
195: markDCPNumberContext(rChild);
196: return NoType;
197: } else
198: return rType;
199: } else if (theFunction.isNumberVar(varIndex)) {
200: if (rType != NumberType) {
201: n.removeChild(rChild);
202: n.addChildToBack(new Node(Token.TO_DOUBLE, rChild));
203: }
204: n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
205: markDCPNumberContext(rChild);
206: return NumberType;
207: } else {
208: if (rType == NumberType) {
209: if (!convertParameter(rChild)) {
210: n.removeChild(rChild);
211: n.addChildToBack(new Node(Token.TO_OBJECT,
212: rChild));
213: }
214: }
215: return NoType;
216: }
217: }
218: case Token.LE:
219: case Token.LT:
220: case Token.GE:
221: case Token.GT: {
222: Node lChild = n.getFirstChild();
223: Node rChild = lChild.getNext();
224: int lType = rewriteForNumberVariables(lChild);
225: int rType = rewriteForNumberVariables(rChild);
226: markDCPNumberContext(lChild);
227: markDCPNumberContext(rChild);
228:
229: if (convertParameter(lChild)) {
230: if (convertParameter(rChild)) {
231: return NoType;
232: } else if (rType == NumberType) {
233: n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
234: }
235: } else if (convertParameter(rChild)) {
236: if (lType == NumberType) {
237: n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
238: }
239: } else {
240: if (lType == NumberType) {
241: if (rType == NumberType) {
242: n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
243: } else {
244: n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
245: }
246: } else {
247: if (rType == NumberType) {
248: n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
249: }
250: }
251: }
252: // we actually build a boolean value
253: return NoType;
254: }
255:
256: case Token.ADD: {
257: Node lChild = n.getFirstChild();
258: Node rChild = lChild.getNext();
259: int lType = rewriteForNumberVariables(lChild);
260: int rType = rewriteForNumberVariables(rChild);
261:
262: if (convertParameter(lChild)) {
263: if (convertParameter(rChild)) {
264: return NoType;
265: } else {
266: if (rType == NumberType) {
267: n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
268: }
269: }
270: } else {
271: if (convertParameter(rChild)) {
272: if (lType == NumberType) {
273: n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
274: }
275: } else {
276: if (lType == NumberType) {
277: if (rType == NumberType) {
278: n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
279: return NumberType;
280: } else {
281: n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
282: }
283: } else {
284: if (rType == NumberType) {
285: n
286: .putIntProp(Node.ISNUMBER_PROP,
287: Node.RIGHT);
288: }
289: }
290: }
291: }
292: return NoType;
293: }
294:
295: case Token.BITXOR:
296: case Token.BITOR:
297: case Token.BITAND:
298: case Token.RSH:
299: case Token.LSH:
300: case Token.SUB:
301: case Token.MUL:
302: case Token.DIV:
303: case Token.MOD: {
304: Node lChild = n.getFirstChild();
305: Node rChild = lChild.getNext();
306: int lType = rewriteForNumberVariables(lChild);
307: int rType = rewriteForNumberVariables(rChild);
308: markDCPNumberContext(lChild);
309: markDCPNumberContext(rChild);
310: if (lType == NumberType) {
311: if (rType == NumberType) {
312: n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
313: return NumberType;
314: } else {
315: if (!convertParameter(rChild)) {
316: n.removeChild(rChild);
317: n.addChildToBack(new Node(Token.TO_DOUBLE,
318: rChild));
319: n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
320: }
321: return NumberType;
322: }
323: } else {
324: if (rType == NumberType) {
325: if (!convertParameter(lChild)) {
326: n.removeChild(lChild);
327: n.addChildToFront(new Node(Token.TO_DOUBLE,
328: lChild));
329: n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
330: }
331: return NumberType;
332: } else {
333: if (!convertParameter(lChild)) {
334: n.removeChild(lChild);
335: n.addChildToFront(new Node(Token.TO_DOUBLE,
336: lChild));
337: }
338: if (!convertParameter(rChild)) {
339: n.removeChild(rChild);
340: n.addChildToBack(new Node(Token.TO_DOUBLE,
341: rChild));
342: }
343: n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
344: return NumberType;
345: }
346: }
347: }
348: case Token.SETELEM:
349: case Token.SETELEM_OP: {
350: Node arrayBase = n.getFirstChild();
351: Node arrayIndex = arrayBase.getNext();
352: Node rValue = arrayIndex.getNext();
353: int baseType = rewriteForNumberVariables(arrayBase);
354: if (baseType == NumberType) {// can never happen ???
355: if (!convertParameter(arrayBase)) {
356: n.removeChild(arrayBase);
357: n.addChildToFront(new Node(Token.TO_OBJECT,
358: arrayBase));
359: }
360: }
361: int indexType = rewriteForNumberVariables(arrayIndex);
362: if (indexType == NumberType) {
363: // setting the ISNUMBER_PROP signals the codegen
364: // to use the OptRuntime.setObjectIndex that takes
365: // a double index
366: n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
367: markDCPNumberContext(arrayIndex);
368: }
369: int rValueType = rewriteForNumberVariables(rValue);
370: if (rValueType == NumberType) {
371: if (!convertParameter(rValue)) {
372: n.removeChild(rValue);
373: n.addChildToBack(new Node(Token.TO_OBJECT, rValue));
374: }
375: }
376: return NoType;
377: }
378: case Token.GETELEM: {
379: Node arrayBase = n.getFirstChild();
380: Node arrayIndex = arrayBase.getNext();
381: int baseType = rewriteForNumberVariables(arrayBase);
382: if (baseType == NumberType) {// can never happen ???
383: if (!convertParameter(arrayBase)) {
384: n.removeChild(arrayBase);
385: n.addChildToFront(new Node(Token.TO_OBJECT,
386: arrayBase));
387: }
388: }
389: int indexType = rewriteForNumberVariables(arrayIndex);
390: if (indexType == NumberType) {
391: if (!convertParameter(arrayIndex)) {
392: // setting the ISNUMBER_PROP signals the codegen
393: // to use the OptRuntime.getObjectIndex that takes
394: // a double index
395: n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
396: }
397: }
398: return NoType;
399: }
400: case Token.CALL: {
401: Node child = n.getFirstChild(); // the function node
402: if (child.getType() == Token.GETELEM) {
403: // Optimization of x[0]() is not supported
404: // so bypass GETELEM optimization that
405: // rewriteForNumberVariables would trigger
406: rewriteAsObjectChildren(child, child.getFirstChild());
407: } else {
408: rewriteForNumberVariables(child);
409: }
410: child = child.getNext(); // the first arg
411:
412: OptFunctionNode target = (OptFunctionNode) n
413: .getProp(Node.DIRECTCALL_PROP);
414: if (target != null) {
415: /*
416: we leave each child as a Number if it can be. The codegen will
417: handle moving the pairs of parameters.
418: */
419: while (child != null) {
420: int type = rewriteForNumberVariables(child);
421: if (type == NumberType) {
422: markDCPNumberContext(child);
423: }
424: child = child.getNext();
425: }
426: } else {
427: rewriteAsObjectChildren(n, child);
428: }
429: return NoType;
430: }
431: default: {
432: rewriteAsObjectChildren(n, n.getFirstChild());
433: return NoType;
434: }
435: }
436: }
437:
438: private void rewriteAsObjectChildren(Node n, Node child) {
439: // Force optimized children to be objects
440: while (child != null) {
441: Node nextChild = child.getNext();
442: int type = rewriteForNumberVariables(child);
443: if (type == NumberType) {
444: if (!convertParameter(child)) {
445: n.removeChild(child);
446: Node nuChild = new Node(Token.TO_OBJECT, child);
447: if (nextChild == null)
448: n.addChildToBack(nuChild);
449: else
450: n.addChildBefore(nuChild, nextChild);
451: }
452: }
453: child = nextChild;
454: }
455: }
456:
457: private static void buildStatementList_r(Node node,
458: ObjArray statements) {
459: int type = node.getType();
460: if (type == Token.BLOCK || type == Token.LOCAL_BLOCK
461: || type == Token.LOOP || type == Token.FUNCTION) {
462: Node child = node.getFirstChild();
463: while (child != null) {
464: buildStatementList_r(child, statements);
465: child = child.getNext();
466: }
467: } else {
468: statements.add(node);
469: }
470: }
471:
472: private boolean inDirectCallFunction;
473: OptFunctionNode theFunction;
474: private boolean parameterUsedInNumberContext;
475: }
|