Make first real commit: copy of CaRMetal 4.2.8
This commit is contained in:
parent
002acfc88e
commit
c312811084
1120 changed files with 226843 additions and 1 deletions
619
org/mozilla/javascript/optimizer/Block.java
Normal file
619
org/mozilla/javascript/optimizer/Block.java
Normal file
|
@ -0,0 +1,619 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Rhino code, released
|
||||
* May 6, 1999.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1997-1999
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* Igor Bukanov
|
||||
* Roger Lawrence
|
||||
* Cameron McCormack
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* the GNU General Public License Version 2 or later (the "GPL"), in which
|
||||
* case the provisions of the GPL are applicable instead of those above. If
|
||||
* you wish to allow use of your version of this file only under the terms of
|
||||
* the GPL and not to allow others to use your version of this file under the
|
||||
* MPL, indicate your decision by deleting the provisions above and replacing
|
||||
* them with the notice and other provisions required by the GPL. If you do
|
||||
* not delete the provisions above, a recipient may use your version of this
|
||||
* file under either the MPL or the GPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.javascript.optimizer;
|
||||
|
||||
import org.mozilla.javascript.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
class Block
|
||||
{
|
||||
|
||||
private static class FatBlock
|
||||
{
|
||||
|
||||
private static Block[] reduceToArray(ObjToIntMap map)
|
||||
{
|
||||
Block[] result = null;
|
||||
if (!map.isEmpty()) {
|
||||
result = new Block[map.size()];
|
||||
int i = 0;
|
||||
ObjToIntMap.Iterator iter = map.newIterator();
|
||||
for (iter.start(); !iter.done(); iter.next()) {
|
||||
FatBlock fb = (FatBlock)(iter.getKey());
|
||||
result[i++] = fb.realBlock;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void addSuccessor(FatBlock b) { successors.put(b, 0); }
|
||||
void addPredecessor(FatBlock b) { predecessors.put(b, 0); }
|
||||
|
||||
Block[] getSuccessors() { return reduceToArray(successors); }
|
||||
Block[] getPredecessors() { return reduceToArray(predecessors); }
|
||||
|
||||
// all the Blocks that come immediately after this
|
||||
private ObjToIntMap successors = new ObjToIntMap();
|
||||
// all the Blocks that come immediately before this
|
||||
private ObjToIntMap predecessors = new ObjToIntMap();
|
||||
|
||||
Block realBlock;
|
||||
}
|
||||
|
||||
Block(int startNodeIndex, int endNodeIndex)
|
||||
{
|
||||
itsStartNodeIndex = startNodeIndex;
|
||||
itsEndNodeIndex = endNodeIndex;
|
||||
}
|
||||
|
||||
static void runFlowAnalyzes(OptFunctionNode fn, Node[] statementNodes)
|
||||
{
|
||||
int paramCount = fn.fnode.getParamCount();
|
||||
int varCount = fn.fnode.getParamAndVarCount();
|
||||
int[] varTypes = new int[varCount];
|
||||
// If the variable is a parameter, it could have any type.
|
||||
for (int i = 0; i != paramCount; ++i) {
|
||||
varTypes[i] = Optimizer.AnyType;
|
||||
}
|
||||
// If the variable is from a "var" statement, its typeEvent will be set
|
||||
// when we see the setVar node.
|
||||
for (int i = paramCount; i != varCount; ++i) {
|
||||
varTypes[i] = Optimizer.NoType;
|
||||
}
|
||||
|
||||
Block[] theBlocks = buildBlocks(statementNodes);
|
||||
|
||||
if (DEBUG) {
|
||||
++debug_blockCount;
|
||||
System.out.println("-------------------"+fn.fnode.getFunctionName()+" "+debug_blockCount+"--------");
|
||||
System.out.println(toString(theBlocks, statementNodes));
|
||||
}
|
||||
|
||||
reachingDefDataFlow(fn, statementNodes, theBlocks, varTypes);
|
||||
typeFlow(fn, statementNodes, theBlocks, varTypes);
|
||||
|
||||
if (DEBUG) {
|
||||
for (int i = 0; i < theBlocks.length; i++) {
|
||||
System.out.println("For block " + theBlocks[i].itsBlockID);
|
||||
theBlocks[i].printLiveOnEntrySet(fn);
|
||||
}
|
||||
System.out.println("Variable Table, size = " + varCount);
|
||||
for (int i = 0; i != varCount; i++) {
|
||||
System.out.println("["+i+"] type: "+varTypes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = paramCount; i != varCount; i++) {
|
||||
if (varTypes[i] == Optimizer.NumberType) {
|
||||
fn.setIsNumberVar(i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static Block[] buildBlocks(Node[] statementNodes)
|
||||
{
|
||||
// a mapping from each target node to the block it begins
|
||||
Map<Node,FatBlock> theTargetBlocks = new HashMap<Node,FatBlock>();
|
||||
ObjArray theBlocks = new ObjArray();
|
||||
|
||||
// there's a block that starts at index 0
|
||||
int beginNodeIndex = 0;
|
||||
|
||||
for (int i = 0; i < statementNodes.length; i++) {
|
||||
switch (statementNodes[i].getType()) {
|
||||
case Token.TARGET :
|
||||
{
|
||||
if (i != beginNodeIndex) {
|
||||
FatBlock fb = newFatBlock(beginNodeIndex, i - 1);
|
||||
if (statementNodes[beginNodeIndex].getType()
|
||||
== Token.TARGET)
|
||||
theTargetBlocks.put(statementNodes[beginNodeIndex], fb);
|
||||
theBlocks.add(fb);
|
||||
// start the next block at this node
|
||||
beginNodeIndex = i;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Token.IFNE :
|
||||
case Token.IFEQ :
|
||||
case Token.GOTO :
|
||||
{
|
||||
FatBlock fb = newFatBlock(beginNodeIndex, i);
|
||||
if (statementNodes[beginNodeIndex].getType()
|
||||
== Token.TARGET)
|
||||
theTargetBlocks.put(statementNodes[beginNodeIndex], fb);
|
||||
theBlocks.add(fb);
|
||||
// start the next block at the next node
|
||||
beginNodeIndex = i + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (beginNodeIndex != statementNodes.length) {
|
||||
FatBlock fb = newFatBlock(beginNodeIndex, statementNodes.length - 1);
|
||||
if (statementNodes[beginNodeIndex].getType() == Token.TARGET)
|
||||
theTargetBlocks.put(statementNodes[beginNodeIndex], fb);
|
||||
theBlocks.add(fb);
|
||||
}
|
||||
|
||||
// build successor and predecessor links
|
||||
|
||||
for (int i = 0; i < theBlocks.size(); i++) {
|
||||
FatBlock fb = (FatBlock)(theBlocks.get(i));
|
||||
|
||||
Node blockEndNode = statementNodes[fb.realBlock.itsEndNodeIndex];
|
||||
int blockEndNodeType = blockEndNode.getType();
|
||||
|
||||
if ((blockEndNodeType != Token.GOTO)
|
||||
&& (i < (theBlocks.size() - 1))) {
|
||||
FatBlock fallThruTarget = (FatBlock)(theBlocks.get(i + 1));
|
||||
fb.addSuccessor(fallThruTarget);
|
||||
fallThruTarget.addPredecessor(fb);
|
||||
}
|
||||
|
||||
|
||||
if ( (blockEndNodeType == Token.IFNE)
|
||||
|| (blockEndNodeType == Token.IFEQ)
|
||||
|| (blockEndNodeType == Token.GOTO) ) {
|
||||
Node target = ((Node.Jump)blockEndNode).target;
|
||||
FatBlock branchTargetBlock = theTargetBlocks.get(target);
|
||||
target.putProp(Node.TARGETBLOCK_PROP,
|
||||
branchTargetBlock.realBlock);
|
||||
fb.addSuccessor(branchTargetBlock);
|
||||
branchTargetBlock.addPredecessor(fb);
|
||||
}
|
||||
}
|
||||
|
||||
Block[] result = new Block[theBlocks.size()];
|
||||
|
||||
for (int i = 0; i < theBlocks.size(); i++) {
|
||||
FatBlock fb = (FatBlock)(theBlocks.get(i));
|
||||
Block b = fb.realBlock;
|
||||
b.itsSuccessors = fb.getSuccessors();
|
||||
b.itsPredecessors = fb.getPredecessors();
|
||||
b.itsBlockID = i;
|
||||
result[i] = b;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static FatBlock newFatBlock(int startNodeIndex, int endNodeIndex)
|
||||
{
|
||||
FatBlock fb = new FatBlock();
|
||||
fb.realBlock = new Block(startNodeIndex, endNodeIndex);
|
||||
return fb;
|
||||
}
|
||||
|
||||
private static String toString(Block[] blockList, Node[] statementNodes)
|
||||
{
|
||||
if (!DEBUG) return null;
|
||||
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
|
||||
pw.println(blockList.length + " Blocks");
|
||||
for (int i = 0; i < blockList.length; i++) {
|
||||
Block b = blockList[i];
|
||||
pw.println("#" + b.itsBlockID);
|
||||
pw.println("from " + b.itsStartNodeIndex
|
||||
+ " "
|
||||
+ statementNodes[b.itsStartNodeIndex].toString());
|
||||
pw.println("thru " + b.itsEndNodeIndex
|
||||
+ " "
|
||||
+ statementNodes[b.itsEndNodeIndex].toString());
|
||||
pw.print("Predecessors ");
|
||||
if (b.itsPredecessors != null) {
|
||||
for (int j = 0; j < b.itsPredecessors.length; j++)
|
||||
pw.print(b.itsPredecessors[j].itsBlockID + " ");
|
||||
pw.println();
|
||||
}
|
||||
else
|
||||
pw.println("none");
|
||||
pw.print("Successors ");
|
||||
if (b.itsSuccessors != null) {
|
||||
for (int j = 0; j < b.itsSuccessors.length; j++)
|
||||
pw.print(b.itsSuccessors[j].itsBlockID + " ");
|
||||
pw.println();
|
||||
}
|
||||
else
|
||||
pw.println("none");
|
||||
}
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
private static void reachingDefDataFlow(OptFunctionNode fn, Node[] statementNodes, Block theBlocks[], int[] varTypes)
|
||||
{
|
||||
/*
|
||||
initialize the liveOnEntry and liveOnExit sets, then discover the variables
|
||||
that are def'd by each function, and those that are used before being def'd
|
||||
(hence liveOnEntry)
|
||||
*/
|
||||
for (int i = 0; i < theBlocks.length; i++) {
|
||||
theBlocks[i].initLiveOnEntrySets(fn, statementNodes);
|
||||
}
|
||||
/*
|
||||
this visits every block starting at the last, re-adding the predecessors of
|
||||
any block whose inputs change as a result of the dataflow.
|
||||
REMIND, better would be to visit in CFG postorder
|
||||
*/
|
||||
boolean visit[] = new boolean[theBlocks.length];
|
||||
boolean doneOnce[] = new boolean[theBlocks.length];
|
||||
int vIndex = theBlocks.length - 1;
|
||||
boolean needRescan = false;
|
||||
visit[vIndex] = true;
|
||||
while (true) {
|
||||
if (visit[vIndex] || !doneOnce[vIndex]) {
|
||||
doneOnce[vIndex] = true;
|
||||
visit[vIndex] = false;
|
||||
if (theBlocks[vIndex].doReachedUseDataFlow()) {
|
||||
Block pred[] = theBlocks[vIndex].itsPredecessors;
|
||||
if (pred != null) {
|
||||
for (int i = 0; i < pred.length; i++) {
|
||||
int index = pred[i].itsBlockID;
|
||||
visit[index] = true;
|
||||
needRescan |= (index > vIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (vIndex == 0) {
|
||||
if (needRescan) {
|
||||
vIndex = theBlocks.length - 1;
|
||||
needRescan = false;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
vIndex--;
|
||||
}
|
||||
/*
|
||||
if any variable is live on entry to block 0, we have to mark it as
|
||||
not jRegable - since it means that someone is trying to access the
|
||||
'undefined'-ness of that variable.
|
||||
*/
|
||||
|
||||
theBlocks[0].markAnyTypeVariables(varTypes);
|
||||
}
|
||||
|
||||
private static void typeFlow(OptFunctionNode fn, Node[] statementNodes, Block theBlocks[], int[] varTypes)
|
||||
{
|
||||
boolean visit[] = new boolean[theBlocks.length];
|
||||
boolean doneOnce[] = new boolean[theBlocks.length];
|
||||
int vIndex = 0;
|
||||
boolean needRescan = false;
|
||||
visit[vIndex] = true;
|
||||
while (true) {
|
||||
if (visit[vIndex] || !doneOnce[vIndex]) {
|
||||
doneOnce[vIndex] = true;
|
||||
visit[vIndex] = false;
|
||||
if (theBlocks[vIndex].doTypeFlow(fn, statementNodes, varTypes))
|
||||
{
|
||||
Block succ[] = theBlocks[vIndex].itsSuccessors;
|
||||
if (succ != null) {
|
||||
for (int i = 0; i < succ.length; i++) {
|
||||
int index = succ[i].itsBlockID;
|
||||
visit[index] = true;
|
||||
needRescan |= (index < vIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (vIndex == (theBlocks.length - 1)) {
|
||||
if (needRescan) {
|
||||
vIndex = 0;
|
||||
needRescan = false;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
vIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean assignType(int[] varTypes, int index, int type)
|
||||
{
|
||||
return type != (varTypes[index] |= type);
|
||||
}
|
||||
|
||||
private void markAnyTypeVariables(int[] varTypes)
|
||||
{
|
||||
for (int i = 0; i != varTypes.length; i++) {
|
||||
if (itsLiveOnEntrySet.test(i)) {
|
||||
assignType(varTypes, i, Optimizer.AnyType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
We're tracking uses and defs - in order to
|
||||
build the def set and to identify the last use
|
||||
nodes.
|
||||
|
||||
The itsNotDefSet is built reversed then flipped later.
|
||||
|
||||
*/
|
||||
private void lookForVariableAccess(OptFunctionNode fn, Node n)
|
||||
{
|
||||
switch (n.getType()) {
|
||||
case Token.DEC :
|
||||
case Token.INC :
|
||||
{
|
||||
Node child = n.getFirstChild();
|
||||
if (child.getType() == Token.GETVAR) {
|
||||
int varIndex = fn.getVarIndex(child);
|
||||
if (!itsNotDefSet.test(varIndex))
|
||||
itsUseBeforeDefSet.set(varIndex);
|
||||
itsNotDefSet.set(varIndex);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Token.SETVAR :
|
||||
{
|
||||
Node lhs = n.getFirstChild();
|
||||
Node rhs = lhs.getNext();
|
||||
lookForVariableAccess(fn, rhs);
|
||||
itsNotDefSet.set(fn.getVarIndex(n));
|
||||
}
|
||||
break;
|
||||
case Token.GETVAR :
|
||||
{
|
||||
int varIndex = fn.getVarIndex(n);
|
||||
if (!itsNotDefSet.test(varIndex))
|
||||
itsUseBeforeDefSet.set(varIndex);
|
||||
}
|
||||
break;
|
||||
default :
|
||||
Node child = n.getFirstChild();
|
||||
while (child != null) {
|
||||
lookForVariableAccess(fn, child);
|
||||
child = child.getNext();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
build the live on entry/exit sets.
|
||||
Then walk the trees looking for defs/uses of variables
|
||||
and build the def and useBeforeDef sets.
|
||||
*/
|
||||
private void initLiveOnEntrySets(OptFunctionNode fn, Node[] statementNodes)
|
||||
{
|
||||
int listLength = fn.getVarCount();
|
||||
itsUseBeforeDefSet = new DataFlowBitSet(listLength);
|
||||
itsNotDefSet = new DataFlowBitSet(listLength);
|
||||
itsLiveOnEntrySet = new DataFlowBitSet(listLength);
|
||||
itsLiveOnExitSet = new DataFlowBitSet(listLength);
|
||||
for (int i = itsStartNodeIndex; i <= itsEndNodeIndex; i++) {
|
||||
Node n = statementNodes[i];
|
||||
lookForVariableAccess(fn, n);
|
||||
}
|
||||
itsNotDefSet.not(); // truth in advertising
|
||||
}
|
||||
|
||||
/*
|
||||
the liveOnEntry of each successor is the liveOnExit for this block.
|
||||
The liveOnEntry for this block is -
|
||||
liveOnEntry = liveOnExit - defsInThisBlock + useBeforeDefsInThisBlock
|
||||
|
||||
*/
|
||||
private boolean doReachedUseDataFlow()
|
||||
{
|
||||
itsLiveOnExitSet.clear();
|
||||
if (itsSuccessors != null)
|
||||
for (int i = 0; i < itsSuccessors.length; i++)
|
||||
itsLiveOnExitSet.or(itsSuccessors[i].itsLiveOnEntrySet);
|
||||
return itsLiveOnEntrySet.df2(itsLiveOnExitSet,
|
||||
itsUseBeforeDefSet, itsNotDefSet);
|
||||
}
|
||||
|
||||
/*
|
||||
the type of an expression is relatively unknown. Cases we can be sure
|
||||
about are -
|
||||
Literals,
|
||||
Arithmetic operations - always return a Number
|
||||
*/
|
||||
private static int findExpressionType(OptFunctionNode fn, Node n,
|
||||
int[] varTypes)
|
||||
{
|
||||
switch (n.getType()) {
|
||||
case Token.NUMBER:
|
||||
return Optimizer.NumberType;
|
||||
|
||||
case Token.CALL:
|
||||
case Token.NEW:
|
||||
case Token.REF_CALL:
|
||||
return Optimizer.AnyType;
|
||||
|
||||
case Token.GETELEM:
|
||||
return Optimizer.AnyType;
|
||||
|
||||
case Token.GETVAR:
|
||||
return varTypes[fn.getVarIndex(n)];
|
||||
|
||||
case Token.INC:
|
||||
case Token.DEC:
|
||||
case Token.MUL:
|
||||
case Token.DIV:
|
||||
case Token.MOD:
|
||||
case Token.BITOR:
|
||||
case Token.BITXOR:
|
||||
case Token.BITAND:
|
||||
case Token.LSH:
|
||||
case Token.RSH:
|
||||
case Token.URSH:
|
||||
case Token.SUB:
|
||||
case Token.POS:
|
||||
case Token.NEG:
|
||||
return Optimizer.NumberType;
|
||||
|
||||
case Token.ARRAYLIT:
|
||||
case Token.OBJECTLIT:
|
||||
return Optimizer.AnyType; // XXX: actually, we know it's not
|
||||
// number, but no type yet for that
|
||||
|
||||
case Token.ADD: {
|
||||
// if the lhs & rhs are known to be numbers, we can be sure that's
|
||||
// the result, otherwise it could be a string.
|
||||
Node child = n.getFirstChild();
|
||||
int lType = findExpressionType(fn, child, varTypes);
|
||||
int rType = findExpressionType(fn, child.getNext(), varTypes);
|
||||
return lType | rType; // we're not distinguishing strings yet
|
||||
}
|
||||
}
|
||||
|
||||
Node child = n.getFirstChild();
|
||||
if (child == null) {
|
||||
return Optimizer.AnyType;
|
||||
} else {
|
||||
int result = Optimizer.NoType;
|
||||
while (child != null) {
|
||||
result |= findExpressionType(fn, child, varTypes);
|
||||
child = child.getNext();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean findDefPoints(OptFunctionNode fn, Node n,
|
||||
int[] varTypes)
|
||||
{
|
||||
boolean result = false;
|
||||
Node child = n.getFirstChild();
|
||||
switch (n.getType()) {
|
||||
default :
|
||||
while (child != null) {
|
||||
result |= findDefPoints(fn, child, varTypes);
|
||||
child = child.getNext();
|
||||
}
|
||||
break;
|
||||
case Token.DEC :
|
||||
case Token.INC :
|
||||
if (child.getType() == Token.GETVAR) {
|
||||
// theVar is a Number now
|
||||
int i = fn.getVarIndex(child);
|
||||
result |= assignType(varTypes, i, Optimizer.NumberType);
|
||||
}
|
||||
break;
|
||||
case Token.SETPROP :
|
||||
case Token.SETPROP_OP :
|
||||
if (child.getType() == Token.GETVAR) {
|
||||
int i = fn.getVarIndex(child);
|
||||
assignType(varTypes, i, Optimizer.AnyType);
|
||||
}
|
||||
while (child != null) {
|
||||
result |= findDefPoints(fn, child, varTypes);
|
||||
child = child.getNext();
|
||||
}
|
||||
break;
|
||||
case Token.SETVAR : {
|
||||
Node rValue = child.getNext();
|
||||
int theType = findExpressionType(fn, rValue, varTypes);
|
||||
int i = fn.getVarIndex(n);
|
||||
result |= assignType(varTypes, i, theType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean doTypeFlow(OptFunctionNode fn, Node[] statementNodes,
|
||||
int[] varTypes)
|
||||
{
|
||||
boolean changed = false;
|
||||
|
||||
for (int i = itsStartNodeIndex; i <= itsEndNodeIndex; i++) {
|
||||
Node n = statementNodes[i];
|
||||
if (n != null)
|
||||
changed |= findDefPoints(fn, n, varTypes);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private void printLiveOnEntrySet(OptFunctionNode fn)
|
||||
{
|
||||
if (DEBUG) {
|
||||
for (int i = 0; i < fn.getVarCount(); i++) {
|
||||
String name = fn.fnode.getParamOrVarName(i);
|
||||
if (itsUseBeforeDefSet.test(i))
|
||||
System.out.println(name + " is used before def'd");
|
||||
if (itsNotDefSet.test(i))
|
||||
System.out.println(name + " is not def'd");
|
||||
if (itsLiveOnEntrySet.test(i))
|
||||
System.out.println(name + " is live on entry");
|
||||
if (itsLiveOnExitSet.test(i))
|
||||
System.out.println(name + " is live on exit");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// all the Blocks that come immediately after this
|
||||
private Block[] itsSuccessors;
|
||||
// all the Blocks that come immediately before this
|
||||
private Block[] itsPredecessors;
|
||||
|
||||
private int itsStartNodeIndex; // the Node at the start of the block
|
||||
private int itsEndNodeIndex; // the Node at the end of the block
|
||||
|
||||
private int itsBlockID; // a unique index for each block
|
||||
|
||||
// reaching def bit sets -
|
||||
private DataFlowBitSet itsLiveOnEntrySet;
|
||||
private DataFlowBitSet itsLiveOnExitSet;
|
||||
private DataFlowBitSet itsUseBeforeDefSet;
|
||||
private DataFlowBitSet itsNotDefSet;
|
||||
|
||||
static final boolean DEBUG = false;
|
||||
private static int debug_blockCount;
|
||||
|
||||
}
|
||||
|
211
org/mozilla/javascript/optimizer/ClassCompiler.java
Normal file
211
org/mozilla/javascript/optimizer/ClassCompiler.java
Normal file
|
@ -0,0 +1,211 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Rhino code, released
|
||||
* May 6, 1999.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1997-2000
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Igor Bukanov
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* the GNU General Public License Version 2 or later (the "GPL"), in which
|
||||
* case the provisions of the GPL are applicable instead of those above. If
|
||||
* you wish to allow use of your version of this file only under the terms of
|
||||
* the GPL and not to allow others to use your version of this file under the
|
||||
* MPL, indicate your decision by deleting the provisions above and replacing
|
||||
* them with the notice and other provisions required by the GPL. If you do
|
||||
* not delete the provisions above, a recipient may use your version of this
|
||||
* file under either the MPL or the GPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
package org.mozilla.javascript.optimizer;
|
||||
|
||||
import org.mozilla.javascript.*;
|
||||
|
||||
/**
|
||||
* Generates class files from script sources.
|
||||
*
|
||||
* since 1.5 Release 5
|
||||
* @author Igor Bukanov
|
||||
*/
|
||||
|
||||
public class ClassCompiler
|
||||
{
|
||||
/**
|
||||
* Construct ClassCompiler that uses the specified compiler environment
|
||||
* when generating classes.
|
||||
*/
|
||||
public ClassCompiler(CompilerEnvirons compilerEnv)
|
||||
{
|
||||
if (compilerEnv == null) throw new IllegalArgumentException();
|
||||
this.compilerEnv = compilerEnv;
|
||||
this.mainMethodClassName = Codegen.DEFAULT_MAIN_METHOD_CLASS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class name to use for main method implementation.
|
||||
* The class must have a method matching
|
||||
* <tt>public static void main(Script sc, String[] args)</tt>, it will be
|
||||
* called when <tt>main(String[] args)</tt> is called in the generated
|
||||
* class. The class name should be fully qulified name and include the
|
||||
* package name like in <tt>org.foo.Bar<tt>.
|
||||
*/
|
||||
public void setMainMethodClass(String className)
|
||||
{
|
||||
// XXX Should this check for a valid class name?
|
||||
mainMethodClassName = className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the class for main method implementation.
|
||||
* @see #setMainMethodClass(String)
|
||||
*/
|
||||
public String getMainMethodClass()
|
||||
{
|
||||
return mainMethodClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the compiler environment the compiler uses.
|
||||
*/
|
||||
public CompilerEnvirons getCompilerEnv()
|
||||
{
|
||||
return compilerEnv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class that the generated target will extend.
|
||||
*/
|
||||
public Class<?> getTargetExtends()
|
||||
{
|
||||
return targetExtends;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class that the generated target will extend.
|
||||
*
|
||||
* @param extendsClass the class it extends
|
||||
*/
|
||||
public void setTargetExtends(Class<?> extendsClass)
|
||||
{
|
||||
targetExtends = extendsClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the interfaces that the generated target will implement.
|
||||
*/
|
||||
public Class<?>[] getTargetImplements()
|
||||
{
|
||||
return targetImplements == null ? null : (Class[])targetImplements.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the interfaces that the generated target will implement.
|
||||
*
|
||||
* @param implementsClasses an array of Class objects, one for each
|
||||
* interface the target will extend
|
||||
*/
|
||||
public void setTargetImplements(Class<?>[] implementsClasses)
|
||||
{
|
||||
targetImplements = implementsClasses == null ? null : (Class[])implementsClasses.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build class name for a auxiliary class generated by compiler.
|
||||
* If the compiler needs to generate extra classes beyond the main class,
|
||||
* it will call this function to build the auxiliary class name.
|
||||
* The default implementation simply appends auxMarker to mainClassName
|
||||
* but this can be overridden.
|
||||
*/
|
||||
protected String makeAuxiliaryClassName(String mainClassName,
|
||||
String auxMarker)
|
||||
{
|
||||
return mainClassName+auxMarker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile JavaScript source into one or more Java class files.
|
||||
* The first compiled class will have name mainClassName.
|
||||
* If the results of {@link #getTargetExtends()} or
|
||||
* {@link #getTargetImplements()} are not null, then the first compiled
|
||||
* class will extend the specified super class and implement
|
||||
* specified interfaces.
|
||||
*
|
||||
* @return array where elements with even indexes specifies class name
|
||||
* and the following odd index gives class file body as byte[]
|
||||
* array. The initial element of the array always holds
|
||||
* mainClassName and array[1] holds its byte code.
|
||||
*/
|
||||
public Object[] compileToClassFiles(String source,
|
||||
String sourceLocation,
|
||||
int lineno,
|
||||
String mainClassName)
|
||||
{
|
||||
Parser p = new Parser(compilerEnv, compilerEnv.getErrorReporter());
|
||||
ScriptOrFnNode tree = p.parse(source, sourceLocation, lineno);
|
||||
String encodedSource = p.getEncodedSource();
|
||||
|
||||
Class<?> superClass = getTargetExtends();
|
||||
Class<?>[] interfaces = getTargetImplements();
|
||||
String scriptClassName;
|
||||
boolean isPrimary = (interfaces == null && superClass == null);
|
||||
if (isPrimary) {
|
||||
scriptClassName = mainClassName;
|
||||
} else {
|
||||
scriptClassName = makeAuxiliaryClassName(mainClassName, "1");
|
||||
}
|
||||
|
||||
Codegen codegen = new Codegen();
|
||||
codegen.setMainMethodClass(mainMethodClassName);
|
||||
byte[] scriptClassBytes
|
||||
= codegen.compileToClassFile(compilerEnv, scriptClassName,
|
||||
tree, encodedSource,
|
||||
false);
|
||||
|
||||
if (isPrimary) {
|
||||
return new Object[] { scriptClassName, scriptClassBytes };
|
||||
}
|
||||
int functionCount = tree.getFunctionCount();
|
||||
ObjToIntMap functionNames = new ObjToIntMap(functionCount);
|
||||
for (int i = 0; i != functionCount; ++i) {
|
||||
FunctionNode ofn = tree.getFunctionNode(i);
|
||||
String name = ofn.getFunctionName();
|
||||
if (name != null && name.length() != 0) {
|
||||
functionNames.put(name, ofn.getParamCount());
|
||||
}
|
||||
}
|
||||
if (superClass == null) {
|
||||
superClass = ScriptRuntime.ObjectClass;
|
||||
}
|
||||
byte[] mainClassBytes
|
||||
= JavaAdapter.createAdapterCode(
|
||||
functionNames, mainClassName,
|
||||
superClass, interfaces, scriptClassName);
|
||||
|
||||
return new Object[] { mainClassName, mainClassBytes,
|
||||
scriptClassName, scriptClassBytes };
|
||||
}
|
||||
|
||||
private String mainMethodClassName;
|
||||
private CompilerEnvirons compilerEnv;
|
||||
private Class<?> targetExtends;
|
||||
private Class<?>[] targetImplements;
|
||||
|
||||
}
|
||||
|
5048
org/mozilla/javascript/optimizer/Codegen.java
Normal file
5048
org/mozilla/javascript/optimizer/Codegen.java
Normal file
File diff suppressed because it is too large
Load diff
135
org/mozilla/javascript/optimizer/DataFlowBitSet.java
Normal file
135
org/mozilla/javascript/optimizer/DataFlowBitSet.java
Normal file
|
@ -0,0 +1,135 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Rhino code, released
|
||||
* May 6, 1999.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1997-1999
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* Roger Lawrence
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* the GNU General Public License Version 2 or later (the "GPL"), in which
|
||||
* case the provisions of the GPL are applicable instead of those above. If
|
||||
* you wish to allow use of your version of this file only under the terms of
|
||||
* the GPL and not to allow others to use your version of this file under the
|
||||
* MPL, indicate your decision by deleting the provisions above and replacing
|
||||
* them with the notice and other provisions required by the GPL. If you do
|
||||
* not delete the provisions above, a recipient may use your version of this
|
||||
* file under either the MPL or the GPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
|
||||
package org.mozilla.javascript.optimizer;
|
||||
|
||||
class DataFlowBitSet {
|
||||
|
||||
private int itsBits[];
|
||||
private int itsSize;
|
||||
|
||||
DataFlowBitSet(int size)
|
||||
{
|
||||
itsSize = size;
|
||||
itsBits = new int[(size + 31) >> 5];
|
||||
}
|
||||
|
||||
void set(int n)
|
||||
{
|
||||
if (!(0 <= n && n < itsSize)) badIndex(n);
|
||||
itsBits[n >> 5] |= 1 << (n & 31);
|
||||
}
|
||||
|
||||
boolean test(int n)
|
||||
{
|
||||
if (!(0 <= n && n < itsSize)) badIndex(n);
|
||||
return ((itsBits[n >> 5] & (1 << (n & 31))) != 0);
|
||||
}
|
||||
|
||||
void not()
|
||||
{
|
||||
int bitsLength = itsBits.length;
|
||||
for (int i = 0; i < bitsLength; i++)
|
||||
itsBits[i] = ~itsBits[i];
|
||||
}
|
||||
|
||||
void clear(int n)
|
||||
{
|
||||
if (!(0 <= n && n < itsSize)) badIndex(n);
|
||||
itsBits[n >> 5] &= ~(1 << (n & 31));
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
int bitsLength = itsBits.length;
|
||||
for (int i = 0; i < bitsLength; i++)
|
||||
itsBits[i] = 0;
|
||||
}
|
||||
|
||||
void or(DataFlowBitSet b)
|
||||
{
|
||||
int bitsLength = itsBits.length;
|
||||
for (int i = 0; i < bitsLength; i++)
|
||||
itsBits[i] |= b.itsBits[i];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("DataFlowBitSet, size = ");
|
||||
sb.append(itsSize);
|
||||
sb.append('\n');
|
||||
int bitsLength = itsBits.length;
|
||||
for (int i = 0; i < bitsLength; i++) {
|
||||
sb.append(Integer.toHexString(itsBits[i]));
|
||||
sb.append(' ');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
boolean df(DataFlowBitSet in, DataFlowBitSet gen, DataFlowBitSet notKill)
|
||||
{
|
||||
int bitsLength = itsBits.length;
|
||||
boolean changed = false;
|
||||
for (int i = 0; i < bitsLength; i++) {
|
||||
int oldBits = itsBits[i];
|
||||
itsBits[i] = (in.itsBits[i] | gen.itsBits[i]) & notKill.itsBits[i];
|
||||
changed |= (oldBits != itsBits[i]);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
boolean df2(DataFlowBitSet in, DataFlowBitSet gen, DataFlowBitSet notKill)
|
||||
{
|
||||
int bitsLength = itsBits.length;
|
||||
boolean changed = false;
|
||||
for (int i = 0; i < bitsLength; i++) {
|
||||
int oldBits = itsBits[i];
|
||||
itsBits[i] = (in.itsBits[i] & notKill.itsBits[i]) | gen.itsBits[i];
|
||||
changed |= (oldBits != itsBits[i]);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
private void badIndex(int n)
|
||||
{
|
||||
throw new RuntimeException("DataFlowBitSet bad index " + n);
|
||||
}
|
||||
}
|
149
org/mozilla/javascript/optimizer/OptFunctionNode.java
Normal file
149
org/mozilla/javascript/optimizer/OptFunctionNode.java
Normal file
|
@ -0,0 +1,149 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Rhino code, released
|
||||
* May 6, 1999.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1997-1999
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* Bob Jervis
|
||||
* Roger Lawrence
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* the GNU General Public License Version 2 or later (the "GPL"), in which
|
||||
* case the provisions of the GPL are applicable instead of those above. If
|
||||
* you wish to allow use of your version of this file only under the terms of
|
||||
* the GPL and not to allow others to use your version of this file under the
|
||||
* MPL, indicate your decision by deleting the provisions above and replacing
|
||||
* them with the notice and other provisions required by the GPL. If you do
|
||||
* not delete the provisions above, a recipient may use your version of this
|
||||
* file under either the MPL or the GPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
package org.mozilla.javascript.optimizer;
|
||||
|
||||
import org.mozilla.javascript.*;
|
||||
|
||||
final class OptFunctionNode
|
||||
{
|
||||
OptFunctionNode(FunctionNode fnode)
|
||||
{
|
||||
this.fnode = fnode;
|
||||
fnode.setCompilerData(this);
|
||||
}
|
||||
|
||||
static OptFunctionNode get(ScriptOrFnNode scriptOrFn, int i)
|
||||
{
|
||||
FunctionNode fnode = scriptOrFn.getFunctionNode(i);
|
||||
return (OptFunctionNode)fnode.getCompilerData();
|
||||
}
|
||||
|
||||
static OptFunctionNode get(ScriptOrFnNode scriptOrFn)
|
||||
{
|
||||
return (OptFunctionNode)scriptOrFn.getCompilerData();
|
||||
}
|
||||
|
||||
boolean isTargetOfDirectCall()
|
||||
{
|
||||
return directTargetIndex >= 0;
|
||||
}
|
||||
|
||||
int getDirectTargetIndex()
|
||||
{
|
||||
return directTargetIndex;
|
||||
}
|
||||
|
||||
void setDirectTargetIndex(int directTargetIndex)
|
||||
{
|
||||
// One time action
|
||||
if (directTargetIndex < 0 || this.directTargetIndex >= 0)
|
||||
Kit.codeBug();
|
||||
this.directTargetIndex = directTargetIndex;
|
||||
}
|
||||
|
||||
void setParameterNumberContext(boolean b)
|
||||
{
|
||||
itsParameterNumberContext = b;
|
||||
}
|
||||
|
||||
boolean getParameterNumberContext()
|
||||
{
|
||||
return itsParameterNumberContext;
|
||||
}
|
||||
|
||||
int getVarCount()
|
||||
{
|
||||
return fnode.getParamAndVarCount();
|
||||
}
|
||||
|
||||
boolean isParameter(int varIndex)
|
||||
{
|
||||
return varIndex < fnode.getParamCount();
|
||||
}
|
||||
|
||||
boolean isNumberVar(int varIndex)
|
||||
{
|
||||
varIndex -= fnode.getParamCount();
|
||||
if (varIndex >= 0 && numberVarFlags != null) {
|
||||
return numberVarFlags[varIndex];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void setIsNumberVar(int varIndex)
|
||||
{
|
||||
varIndex -= fnode.getParamCount();
|
||||
// Can only be used with non-parameters
|
||||
if (varIndex < 0) Kit.codeBug();
|
||||
if (numberVarFlags == null) {
|
||||
int size = fnode.getParamAndVarCount() - fnode.getParamCount();
|
||||
numberVarFlags = new boolean[size];
|
||||
}
|
||||
numberVarFlags[varIndex] = true;
|
||||
}
|
||||
|
||||
int getVarIndex(Node n)
|
||||
{
|
||||
int index = n.getIntProp(Node.VARIABLE_PROP, -1);
|
||||
if (index == -1) {
|
||||
Node node;
|
||||
int type = n.getType();
|
||||
if (type == Token.GETVAR) {
|
||||
node = n;
|
||||
} else if (type == Token.SETVAR ||
|
||||
type == Token.SETCONSTVAR) {
|
||||
node = n.getFirstChild();
|
||||
} else {
|
||||
throw Kit.codeBug();
|
||||
}
|
||||
index = fnode.getIndexForNameNode(node);
|
||||
if (index < 0) throw Kit.codeBug();
|
||||
n.putIntProp(Node.VARIABLE_PROP, index);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
FunctionNode fnode;
|
||||
private boolean[] numberVarFlags;
|
||||
private int directTargetIndex = -1;
|
||||
private boolean itsParameterNumberContext;
|
||||
boolean itsContainsCalls0;
|
||||
boolean itsContainsCalls1;
|
||||
}
|
311
org/mozilla/javascript/optimizer/OptRuntime.java
Normal file
311
org/mozilla/javascript/optimizer/OptRuntime.java
Normal file
|
@ -0,0 +1,311 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Rhino code, released
|
||||
* May 6, 1999.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1997-2000
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* Roger Lawrence
|
||||
* Hannes Wallnoefer
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* the GNU General Public License Version 2 or later (the "GPL"), in which
|
||||
* case the provisions of the GPL are applicable instead of those above. If
|
||||
* you wish to allow use of your version of this file only under the terms of
|
||||
* the GPL and not to allow others to use your version of this file under the
|
||||
* MPL, indicate your decision by deleting the provisions above and replacing
|
||||
* them with the notice and other provisions required by the GPL. If you do
|
||||
* not delete the provisions above, a recipient may use your version of this
|
||||
* file under either the MPL or the GPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
package org.mozilla.javascript.optimizer;
|
||||
|
||||
import org.mozilla.javascript.*;
|
||||
|
||||
public final class OptRuntime extends ScriptRuntime
|
||||
{
|
||||
|
||||
public static final Double zeroObj = new Double(0.0);
|
||||
public static final Double oneObj = new Double(1.0);
|
||||
public static final Double minusOneObj = new Double(-1.0);
|
||||
|
||||
/**
|
||||
* Implement ....() call shrinking optimizer code.
|
||||
*/
|
||||
public static Object call0(Callable fun, Scriptable thisObj,
|
||||
Context cx, Scriptable scope)
|
||||
{
|
||||
return fun.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement ....(arg) call shrinking optimizer code.
|
||||
*/
|
||||
public static Object call1(Callable fun, Scriptable thisObj, Object arg0,
|
||||
Context cx, Scriptable scope)
|
||||
{
|
||||
return fun.call(cx, scope, thisObj, new Object[] { arg0 } );
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement ....(arg0, arg1) call shrinking optimizer code.
|
||||
*/
|
||||
public static Object call2(Callable fun, Scriptable thisObj,
|
||||
Object arg0, Object arg1,
|
||||
Context cx, Scriptable scope)
|
||||
{
|
||||
return fun.call(cx, scope, thisObj, new Object[] { arg0, arg1 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement ....(arg0, arg1, ...) call shrinking optimizer code.
|
||||
*/
|
||||
public static Object callN(Callable fun, Scriptable thisObj,
|
||||
Object[] args,
|
||||
Context cx, Scriptable scope)
|
||||
{
|
||||
return fun.call(cx, scope, thisObj, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement name(args) call shrinking optimizer code.
|
||||
*/
|
||||
public static Object callName(Object[] args, String name,
|
||||
Context cx, Scriptable scope)
|
||||
{
|
||||
Callable f = getNameFunctionAndThis(name, cx, scope);
|
||||
Scriptable thisObj = lastStoredScriptable(cx);
|
||||
return f.call(cx, scope, thisObj, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement name() call shrinking optimizer code.
|
||||
*/
|
||||
public static Object callName0(String name,
|
||||
Context cx, Scriptable scope)
|
||||
{
|
||||
Callable f = getNameFunctionAndThis(name, cx, scope);
|
||||
Scriptable thisObj = lastStoredScriptable(cx);
|
||||
return f.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement x.property() call shrinking optimizer code.
|
||||
*/
|
||||
public static Object callProp0(Object value, String property,
|
||||
Context cx, Scriptable scope)
|
||||
{
|
||||
Callable f = getPropFunctionAndThis(value, property, cx, scope);
|
||||
Scriptable thisObj = lastStoredScriptable(cx);
|
||||
return f.call(cx, scope, thisObj, ScriptRuntime.emptyArgs);
|
||||
}
|
||||
|
||||
public static Object add(Object val1, double val2)
|
||||
{
|
||||
if (val1 instanceof Scriptable)
|
||||
val1 = ((Scriptable) val1).getDefaultValue(null);
|
||||
if (!(val1 instanceof String))
|
||||
return wrapDouble(toNumber(val1) + val2);
|
||||
return ((String)val1).concat(toString(val2));
|
||||
}
|
||||
|
||||
public static Object add(double val1, Object val2)
|
||||
{
|
||||
if (val2 instanceof Scriptable)
|
||||
val2 = ((Scriptable) val2).getDefaultValue(null);
|
||||
if (!(val2 instanceof String))
|
||||
return wrapDouble(toNumber(val2) + val1);
|
||||
return toString(val1).concat((String)val2);
|
||||
}
|
||||
|
||||
public static Object elemIncrDecr(Object obj, double index,
|
||||
Context cx, int incrDecrMask)
|
||||
{
|
||||
return ScriptRuntime.elemIncrDecr(obj, new Double(index), cx,
|
||||
incrDecrMask);
|
||||
}
|
||||
|
||||
public static Object[] padStart(Object[] currentArgs, int count) {
|
||||
Object[] result = new Object[currentArgs.length + count];
|
||||
System.arraycopy(currentArgs, 0, result, count, currentArgs.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void initFunction(NativeFunction fn, int functionType,
|
||||
Scriptable scope, Context cx)
|
||||
{
|
||||
ScriptRuntime.initFunction(cx, scope, fn, functionType, false);
|
||||
}
|
||||
|
||||
public static Object callSpecial(Context cx, Callable fun,
|
||||
Scriptable thisObj, Object[] args,
|
||||
Scriptable scope,
|
||||
Scriptable callerThis, int callType,
|
||||
String fileName, int lineNumber)
|
||||
{
|
||||
return ScriptRuntime.callSpecial(cx, fun, thisObj, args, scope,
|
||||
callerThis, callType,
|
||||
fileName, lineNumber);
|
||||
}
|
||||
|
||||
public static Object newObjectSpecial(Context cx, Object fun,
|
||||
Object[] args, Scriptable scope,
|
||||
Scriptable callerThis, int callType)
|
||||
{
|
||||
return ScriptRuntime.newSpecial(cx, fun, args, scope, callType);
|
||||
}
|
||||
|
||||
public static Double wrapDouble(double num)
|
||||
{
|
||||
if (num == 0.0) {
|
||||
if (1 / num > 0) {
|
||||
// +0.0
|
||||
return zeroObj;
|
||||
}
|
||||
} else if (num == 1.0) {
|
||||
return oneObj;
|
||||
} else if (num == -1.0) {
|
||||
return minusOneObj;
|
||||
} else if (num != num) {
|
||||
return NaNobj;
|
||||
}
|
||||
return new Double(num);
|
||||
}
|
||||
|
||||
static String encodeIntArray(int[] array)
|
||||
{
|
||||
// XXX: this extremely inefficient for small integers
|
||||
if (array == null) { return null; }
|
||||
int n = array.length;
|
||||
char[] buffer = new char[1 + n * 2];
|
||||
buffer[0] = 1;
|
||||
for (int i = 0; i != n; ++i) {
|
||||
int value = array[i];
|
||||
int shift = 1 + i * 2;
|
||||
buffer[shift] = (char)(value >>> 16);
|
||||
buffer[shift + 1] = (char)value;
|
||||
}
|
||||
return new String(buffer);
|
||||
}
|
||||
|
||||
private static int[] decodeIntArray(String str, int arraySize)
|
||||
{
|
||||
// XXX: this extremely inefficient for small integers
|
||||
if (arraySize == 0) {
|
||||
if (str != null) throw new IllegalArgumentException();
|
||||
return null;
|
||||
}
|
||||
if (str.length() != 1 + arraySize * 2 && str.charAt(0) != 1) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
int[] array = new int[arraySize];
|
||||
for (int i = 0; i != arraySize; ++i) {
|
||||
int shift = 1 + i * 2;
|
||||
array[i] = (str.charAt(shift) << 16) | str.charAt(shift + 1);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public static Scriptable newArrayLiteral(Object[] objects,
|
||||
String encodedInts,
|
||||
int skipCount,
|
||||
Context cx,
|
||||
Scriptable scope)
|
||||
{
|
||||
int[] skipIndexces = decodeIntArray(encodedInts, skipCount);
|
||||
return newArrayLiteral(objects, skipIndexces, cx, scope);
|
||||
}
|
||||
|
||||
public static void main(final Script script, final String[] args)
|
||||
{
|
||||
ContextFactory.getGlobal().call(new ContextAction() {
|
||||
public Object run(Context cx)
|
||||
{
|
||||
ScriptableObject global = getGlobal(cx);
|
||||
|
||||
// get the command line arguments and define "arguments"
|
||||
// array in the top-level object
|
||||
Object[] argsCopy = new Object[args.length];
|
||||
System.arraycopy(args, 0, argsCopy, 0, args.length);
|
||||
Scriptable argsObj = cx.newArray(global, argsCopy);
|
||||
global.defineProperty("arguments", argsObj,
|
||||
ScriptableObject.DONTENUM);
|
||||
script.exec(cx, global);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void throwStopIteration(Object obj) {
|
||||
throw new JavaScriptException(
|
||||
NativeIterator.getStopIterationObject((Scriptable)obj), "", 0);
|
||||
}
|
||||
|
||||
public static Scriptable createNativeGenerator(NativeFunction funObj,
|
||||
Scriptable scope,
|
||||
Scriptable thisObj,
|
||||
int maxLocals,
|
||||
int maxStack)
|
||||
{
|
||||
return new NativeGenerator(scope, funObj,
|
||||
new GeneratorState(thisObj, maxLocals, maxStack));
|
||||
}
|
||||
|
||||
public static Object[] getGeneratorStackState(Object obj) {
|
||||
GeneratorState rgs = (GeneratorState) obj;
|
||||
if (rgs.stackState == null)
|
||||
rgs.stackState = new Object[rgs.maxStack];
|
||||
return rgs.stackState;
|
||||
}
|
||||
|
||||
public static Object[] getGeneratorLocalsState(Object obj) {
|
||||
GeneratorState rgs = (GeneratorState) obj;
|
||||
if (rgs.localsState == null)
|
||||
rgs.localsState = new Object[rgs.maxLocals];
|
||||
return rgs.localsState;
|
||||
}
|
||||
|
||||
public static class GeneratorState {
|
||||
static final String CLASS_NAME =
|
||||
"org/mozilla/javascript/optimizer/OptRuntime$GeneratorState";
|
||||
|
||||
public int resumptionPoint;
|
||||
static final String resumptionPoint_NAME = "resumptionPoint";
|
||||
static final String resumptionPoint_TYPE = "I";
|
||||
|
||||
public Scriptable thisObj;
|
||||
static final String thisObj_NAME = "thisObj";
|
||||
static final String thisObj_TYPE =
|
||||
"Lorg/mozilla/javascript/Scriptable;";
|
||||
|
||||
Object[] stackState;
|
||||
Object[] localsState;
|
||||
int maxLocals;
|
||||
int maxStack;
|
||||
|
||||
GeneratorState(Scriptable thisObj, int maxLocals, int maxStack) {
|
||||
this.thisObj = thisObj;
|
||||
this.maxLocals = maxLocals;
|
||||
this.maxStack = maxStack;
|
||||
}
|
||||
}
|
||||
}
|
135
org/mozilla/javascript/optimizer/OptTransformer.java
Normal file
135
org/mozilla/javascript/optimizer/OptTransformer.java
Normal file
|
@ -0,0 +1,135 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Rhino code, released
|
||||
* May 6, 1999.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1997-1999
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* Roger Lawrence
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* the GNU General Public License Version 2 or later (the "GPL"), in which
|
||||
* case the provisions of the GPL are applicable instead of those above. If
|
||||
* you wish to allow use of your version of this file only under the terms of
|
||||
* the GPL and not to allow others to use your version of this file under the
|
||||
* MPL, indicate your decision by deleting the provisions above and replacing
|
||||
* them with the notice and other provisions required by the GPL. If you do
|
||||
* not delete the provisions above, a recipient may use your version of this
|
||||
* file under either the MPL or the GPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
package org.mozilla.javascript.optimizer;
|
||||
|
||||
import org.mozilla.javascript.*;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class performs node transforms to prepare for optimization.
|
||||
*
|
||||
* @see NodeTransformer
|
||||
* @author Norris Boyd
|
||||
*/
|
||||
|
||||
class OptTransformer extends NodeTransformer {
|
||||
|
||||
OptTransformer(Map<String,OptFunctionNode> possibleDirectCalls, ObjArray directCallTargets)
|
||||
{
|
||||
this.possibleDirectCalls = possibleDirectCalls;
|
||||
this.directCallTargets = directCallTargets;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitNew(Node node, ScriptOrFnNode tree) {
|
||||
detectDirectCall(node, tree);
|
||||
super.visitNew(node, tree);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitCall(Node node, ScriptOrFnNode tree) {
|
||||
detectDirectCall(node, tree);
|
||||
super.visitCall(node, tree);
|
||||
}
|
||||
|
||||
private void detectDirectCall(Node node, ScriptOrFnNode tree)
|
||||
{
|
||||
if (tree.getType() == Token.FUNCTION) {
|
||||
Node left = node.getFirstChild();
|
||||
|
||||
// count the arguments
|
||||
int argCount = 0;
|
||||
Node arg = left.getNext();
|
||||
while (arg != null) {
|
||||
arg = arg.getNext();
|
||||
argCount++;
|
||||
}
|
||||
|
||||
if (argCount == 0) {
|
||||
OptFunctionNode.get(tree).itsContainsCalls0 = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Optimize a call site by converting call("a", b, c) into :
|
||||
*
|
||||
* FunctionObjectFor"a" <-- instance variable init'd by constructor
|
||||
*
|
||||
* // this is a DIRECTCALL node
|
||||
* fn = GetProp(tmp = GetBase("a"), "a");
|
||||
* if (fn == FunctionObjectFor"a")
|
||||
* fn.call(tmp, b, c)
|
||||
* else
|
||||
* ScriptRuntime.Call(fn, tmp, b, c)
|
||||
*/
|
||||
if (possibleDirectCalls != null) {
|
||||
String targetName = null;
|
||||
if (left.getType() == Token.NAME) {
|
||||
targetName = left.getString();
|
||||
} else if (left.getType() == Token.GETPROP) {
|
||||
targetName = left.getFirstChild().getNext().getString();
|
||||
} else if (left.getType() == Token.GETPROPNOWARN) {
|
||||
throw Kit.codeBug();
|
||||
}
|
||||
if (targetName != null) {
|
||||
OptFunctionNode ofn;
|
||||
ofn = possibleDirectCalls.get(targetName);
|
||||
if (ofn != null
|
||||
&& argCount == ofn.fnode.getParamCount()
|
||||
&& !ofn.fnode.requiresActivation())
|
||||
{
|
||||
// Refuse to directCall any function with more
|
||||
// than 32 parameters - prevent code explosion
|
||||
// for wacky test cases
|
||||
if (argCount <= 32) {
|
||||
node.putProp(Node.DIRECTCALL_PROP, ofn);
|
||||
if (!ofn.isTargetOfDirectCall()) {
|
||||
int index = directCallTargets.size();
|
||||
directCallTargets.add(ofn);
|
||||
ofn.setDirectTargetIndex(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String,OptFunctionNode> possibleDirectCalls;
|
||||
private ObjArray directCallTargets;
|
||||
}
|
509
org/mozilla/javascript/optimizer/Optimizer.java
Normal file
509
org/mozilla/javascript/optimizer/Optimizer.java
Normal file
|
@ -0,0 +1,509 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Rhino code, released
|
||||
* May 6, 1999.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1997-1999
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* Roger Lawrence
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* the GNU General Public License Version 2 or later (the "GPL"), in which
|
||||
* case the provisions of the GPL are applicable instead of those above. If
|
||||
* you wish to allow use of your version of this file only under the terms of
|
||||
* the GPL and not to allow others to use your version of this file under the
|
||||
* MPL, indicate your decision by deleting the provisions above and replacing
|
||||
* them with the notice and other provisions required by the GPL. If you do
|
||||
* not delete the provisions above, a recipient may use your version of this
|
||||
* file under either the MPL or the GPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
|
||||
package org.mozilla.javascript.optimizer;
|
||||
|
||||
import org.mozilla.javascript.*;
|
||||
|
||||
class Optimizer
|
||||
{
|
||||
|
||||
static final int NoType = 0;
|
||||
static final int NumberType = 1;
|
||||
static final int AnyType = 3;
|
||||
|
||||
// It is assumed that (NumberType | AnyType) == AnyType
|
||||
|
||||
void optimize(ScriptOrFnNode scriptOrFn)
|
||||
{
|
||||
// run on one function at a time for now
|
||||
int functionCount = scriptOrFn.getFunctionCount();
|
||||
for (int i = 0; i != functionCount; ++i) {
|
||||
OptFunctionNode f = OptFunctionNode.get(scriptOrFn, i);
|
||||
optimizeFunction(f);
|
||||
}
|
||||
}
|
||||
|
||||
private void optimizeFunction(OptFunctionNode theFunction)
|
||||
{
|
||||
if (theFunction.fnode.requiresActivation()) return;
|
||||
|
||||
inDirectCallFunction = theFunction.isTargetOfDirectCall();
|
||||
this.theFunction = theFunction;
|
||||
|
||||
ObjArray statementsArray = new ObjArray();
|
||||
buildStatementList_r(theFunction.fnode, statementsArray);
|
||||
Node[] theStatementNodes = new Node[statementsArray.size()];
|
||||
statementsArray.toArray(theStatementNodes);
|
||||
|
||||
Block.runFlowAnalyzes(theFunction, theStatementNodes);
|
||||
|
||||
if (!theFunction.fnode.requiresActivation()) {
|
||||
/*
|
||||
* Now that we know which local vars are in fact always
|
||||
* Numbers, we re-write the tree to take advantage of
|
||||
* that. Any arithmetic or assignment op involving just
|
||||
* Number typed vars is marked so that the codegen will
|
||||
* generate non-object code.
|
||||
*/
|
||||
parameterUsedInNumberContext = false;
|
||||
for (int i = 0; i < theStatementNodes.length; i++) {
|
||||
rewriteForNumberVariables(theStatementNodes[i], NumberType);
|
||||
}
|
||||
theFunction.setParameterNumberContext(parameterUsedInNumberContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Each directCall parameter is passed as a pair of values - an object
|
||||
and a double. The value passed depends on the type of value available at
|
||||
the call site. If a double is available, the object in java/lang/Void.TYPE
|
||||
is passed as the object value, and if an object value is available, then
|
||||
0.0 is passed as the double value.
|
||||
|
||||
The receiving routine always tests the object value before proceeding.
|
||||
If the parameter is being accessed in a 'Number Context' then the code
|
||||
sequence is :
|
||||
if ("parameter_objectValue" == java/lang/Void.TYPE)
|
||||
...fine..., use the parameter_doubleValue
|
||||
else
|
||||
toNumber(parameter_objectValue)
|
||||
|
||||
and if the parameter is being referenced in an Object context, the code is
|
||||
if ("parameter_objectValue" == java/lang/Void.TYPE)
|
||||
new Double(parameter_doubleValue)
|
||||
else
|
||||
...fine..., use the parameter_objectValue
|
||||
|
||||
If the receiving code never uses the doubleValue, it is converted on
|
||||
entry to a Double instead.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
We're referencing a node in a Number context (i.e. we'd prefer it
|
||||
was a double value). If the node is a parameter in a directCall
|
||||
function, mark it as being referenced in this context.
|
||||
*/
|
||||
private void markDCPNumberContext(Node n)
|
||||
{
|
||||
if (inDirectCallFunction && n.getType() == Token.GETVAR) {
|
||||
int varIndex = theFunction.getVarIndex(n);
|
||||
if (theFunction.isParameter(varIndex)) {
|
||||
parameterUsedInNumberContext = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean convertParameter(Node n)
|
||||
{
|
||||
if (inDirectCallFunction && n.getType() == Token.GETVAR) {
|
||||
int varIndex = theFunction.getVarIndex(n);
|
||||
if (theFunction.isParameter(varIndex)) {
|
||||
n.removeProp(Node.ISNUMBER_PROP);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int rewriteForNumberVariables(Node n, int desired)
|
||||
{
|
||||
switch (n.getType()) {
|
||||
case Token.EXPR_VOID : {
|
||||
Node child = n.getFirstChild();
|
||||
int type = rewriteForNumberVariables(child, NumberType);
|
||||
if (type == NumberType)
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
|
||||
return NoType;
|
||||
}
|
||||
case Token.NUMBER :
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
|
||||
return NumberType;
|
||||
|
||||
case Token.GETVAR :
|
||||
{
|
||||
int varIndex = theFunction.getVarIndex(n);
|
||||
if (inDirectCallFunction
|
||||
&& theFunction.isParameter(varIndex)
|
||||
&& desired == NumberType)
|
||||
{
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
|
||||
return NumberType;
|
||||
}
|
||||
else if (theFunction.isNumberVar(varIndex)) {
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
|
||||
return NumberType;
|
||||
}
|
||||
return NoType;
|
||||
}
|
||||
|
||||
case Token.INC :
|
||||
case Token.DEC : {
|
||||
Node child = n.getFirstChild();
|
||||
// "child" will be GETVAR or GETPROP or GETELEM
|
||||
if (child.getType() == Token.GETVAR) {
|
||||
;
|
||||
if (rewriteForNumberVariables(child, NumberType) == NumberType &&
|
||||
!convertParameter(child))
|
||||
{
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
|
||||
markDCPNumberContext(child);
|
||||
return NumberType;
|
||||
}
|
||||
return NoType;
|
||||
}
|
||||
else if (child.getType() == Token.GETELEM) {
|
||||
return rewriteForNumberVariables(child, NumberType);
|
||||
}
|
||||
return NoType;
|
||||
}
|
||||
case Token.SETVAR : {
|
||||
Node lChild = n.getFirstChild();
|
||||
Node rChild = lChild.getNext();
|
||||
int rType = rewriteForNumberVariables(rChild, NumberType);
|
||||
int varIndex = theFunction.getVarIndex(n);
|
||||
if (inDirectCallFunction
|
||||
&& theFunction.isParameter(varIndex))
|
||||
{
|
||||
if (rType == NumberType) {
|
||||
if (!convertParameter(rChild)) {
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
|
||||
return NumberType;
|
||||
}
|
||||
markDCPNumberContext(rChild);
|
||||
return NoType;
|
||||
}
|
||||
else
|
||||
return rType;
|
||||
}
|
||||
else if (theFunction.isNumberVar(varIndex)) {
|
||||
if (rType != NumberType) {
|
||||
n.removeChild(rChild);
|
||||
n.addChildToBack(
|
||||
new Node(Token.TO_DOUBLE, rChild));
|
||||
}
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
|
||||
markDCPNumberContext(rChild);
|
||||
return NumberType;
|
||||
}
|
||||
else {
|
||||
if (rType == NumberType) {
|
||||
if (!convertParameter(rChild)) {
|
||||
n.removeChild(rChild);
|
||||
n.addChildToBack(
|
||||
new Node(Token.TO_OBJECT, rChild));
|
||||
}
|
||||
}
|
||||
return NoType;
|
||||
}
|
||||
}
|
||||
case Token.LE :
|
||||
case Token.LT :
|
||||
case Token.GE :
|
||||
case Token.GT : {
|
||||
Node lChild = n.getFirstChild();
|
||||
Node rChild = lChild.getNext();
|
||||
int lType = rewriteForNumberVariables(lChild, NumberType);
|
||||
int rType = rewriteForNumberVariables(rChild, NumberType);
|
||||
markDCPNumberContext(lChild);
|
||||
markDCPNumberContext(rChild);
|
||||
|
||||
if (convertParameter(lChild)) {
|
||||
if (convertParameter(rChild)) {
|
||||
return NoType;
|
||||
} else if (rType == NumberType) {
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
|
||||
}
|
||||
}
|
||||
else if (convertParameter(rChild)) {
|
||||
if (lType == NumberType) {
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (lType == NumberType) {
|
||||
if (rType == NumberType) {
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
|
||||
}
|
||||
else {
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (rType == NumberType) {
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
// we actually build a boolean value
|
||||
return NoType;
|
||||
}
|
||||
|
||||
case Token.ADD : {
|
||||
Node lChild = n.getFirstChild();
|
||||
Node rChild = lChild.getNext();
|
||||
int lType = rewriteForNumberVariables(lChild, NumberType);
|
||||
int rType = rewriteForNumberVariables(rChild, NumberType);
|
||||
|
||||
|
||||
if (convertParameter(lChild)) {
|
||||
if (convertParameter(rChild)) {
|
||||
return NoType;
|
||||
}
|
||||
else {
|
||||
if (rType == NumberType) {
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (convertParameter(rChild)) {
|
||||
if (lType == NumberType) {
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (lType == NumberType) {
|
||||
if (rType == NumberType) {
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
|
||||
return NumberType;
|
||||
}
|
||||
else {
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (rType == NumberType) {
|
||||
n.putIntProp(Node.ISNUMBER_PROP,
|
||||
Node.RIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NoType;
|
||||
}
|
||||
|
||||
case Token.BITXOR :
|
||||
case Token.BITOR :
|
||||
case Token.BITAND :
|
||||
case Token.RSH :
|
||||
case Token.LSH :
|
||||
case Token.SUB :
|
||||
case Token.MUL :
|
||||
case Token.DIV :
|
||||
case Token.MOD : {
|
||||
Node lChild = n.getFirstChild();
|
||||
Node rChild = lChild.getNext();
|
||||
int lType = rewriteForNumberVariables(lChild, NumberType);
|
||||
int rType = rewriteForNumberVariables(rChild, NumberType);
|
||||
markDCPNumberContext(lChild);
|
||||
markDCPNumberContext(rChild);
|
||||
if (lType == NumberType) {
|
||||
if (rType == NumberType) {
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
|
||||
return NumberType;
|
||||
}
|
||||
else {
|
||||
if (!convertParameter(rChild)) {
|
||||
n.removeChild(rChild);
|
||||
n.addChildToBack(
|
||||
new Node(Token.TO_DOUBLE, rChild));
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
|
||||
}
|
||||
return NumberType;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (rType == NumberType) {
|
||||
if (!convertParameter(lChild)) {
|
||||
n.removeChild(lChild);
|
||||
n.addChildToFront(
|
||||
new Node(Token.TO_DOUBLE, lChild));
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
|
||||
}
|
||||
return NumberType;
|
||||
}
|
||||
else {
|
||||
if (!convertParameter(lChild)) {
|
||||
n.removeChild(lChild);
|
||||
n.addChildToFront(
|
||||
new Node(Token.TO_DOUBLE, lChild));
|
||||
}
|
||||
if (!convertParameter(rChild)) {
|
||||
n.removeChild(rChild);
|
||||
n.addChildToBack(
|
||||
new Node(Token.TO_DOUBLE, rChild));
|
||||
}
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.BOTH);
|
||||
return NumberType;
|
||||
}
|
||||
}
|
||||
}
|
||||
case Token.SETELEM :
|
||||
case Token.SETELEM_OP : {
|
||||
Node arrayBase = n.getFirstChild();
|
||||
Node arrayIndex = arrayBase.getNext();
|
||||
Node rValue = arrayIndex.getNext();
|
||||
int baseType = rewriteForNumberVariables(arrayBase, NumberType);
|
||||
if (baseType == NumberType) {// can never happen ???
|
||||
if (!convertParameter(arrayBase)) {
|
||||
n.removeChild(arrayBase);
|
||||
n.addChildToFront(
|
||||
new Node(Token.TO_OBJECT, arrayBase));
|
||||
}
|
||||
}
|
||||
int indexType = rewriteForNumberVariables(arrayIndex, NumberType);
|
||||
if (indexType == NumberType) {
|
||||
if (!convertParameter(arrayIndex)) {
|
||||
// setting the ISNUMBER_PROP signals the codegen
|
||||
// to use the OptRuntime.setObjectIndex that takes
|
||||
// a double index
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.LEFT);
|
||||
}
|
||||
}
|
||||
int rValueType = rewriteForNumberVariables(rValue, NumberType);
|
||||
if (rValueType == NumberType) {
|
||||
if (!convertParameter(rValue)) {
|
||||
n.removeChild(rValue);
|
||||
n.addChildToBack(
|
||||
new Node(Token.TO_OBJECT, rValue));
|
||||
}
|
||||
}
|
||||
return NoType;
|
||||
}
|
||||
case Token.GETELEM : {
|
||||
Node arrayBase = n.getFirstChild();
|
||||
Node arrayIndex = arrayBase.getNext();
|
||||
int baseType = rewriteForNumberVariables(arrayBase, NumberType);
|
||||
if (baseType == NumberType) {// can never happen ???
|
||||
if (!convertParameter(arrayBase)) {
|
||||
n.removeChild(arrayBase);
|
||||
n.addChildToFront(
|
||||
new Node(Token.TO_OBJECT, arrayBase));
|
||||
}
|
||||
}
|
||||
int indexType = rewriteForNumberVariables(arrayIndex, NumberType);
|
||||
if (indexType == NumberType) {
|
||||
if (!convertParameter(arrayIndex)) {
|
||||
// setting the ISNUMBER_PROP signals the codegen
|
||||
// to use the OptRuntime.getObjectIndex that takes
|
||||
// a double index
|
||||
n.putIntProp(Node.ISNUMBER_PROP, Node.RIGHT);
|
||||
}
|
||||
}
|
||||
return NoType;
|
||||
}
|
||||
case Token.CALL :
|
||||
{
|
||||
Node child = n.getFirstChild(); // the function node
|
||||
// must be an object
|
||||
rewriteAsObjectChildren(child, child.getFirstChild());
|
||||
child = child.getNext(); // the first arg
|
||||
|
||||
OptFunctionNode target
|
||||
= (OptFunctionNode)n.getProp(Node.DIRECTCALL_PROP);
|
||||
if (target != null) {
|
||||
/*
|
||||
we leave each child as a Number if it can be. The codegen will
|
||||
handle moving the pairs of parameters.
|
||||
*/
|
||||
while (child != null) {
|
||||
int type = rewriteForNumberVariables(child, NumberType);
|
||||
if (type == NumberType) {
|
||||
markDCPNumberContext(child);
|
||||
}
|
||||
child = child.getNext();
|
||||
}
|
||||
} else {
|
||||
rewriteAsObjectChildren(n, child);
|
||||
}
|
||||
return NoType;
|
||||
}
|
||||
default : {
|
||||
rewriteAsObjectChildren(n, n.getFirstChild());
|
||||
return NoType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void rewriteAsObjectChildren(Node n, Node child)
|
||||
{
|
||||
// Force optimized children to be objects
|
||||
while (child != null) {
|
||||
Node nextChild = child.getNext();
|
||||
int type = rewriteForNumberVariables(child, NoType);
|
||||
if (type == NumberType) {
|
||||
if (!convertParameter(child)) {
|
||||
n.removeChild(child);
|
||||
Node nuChild = new Node(Token.TO_OBJECT, child);
|
||||
if (nextChild == null)
|
||||
n.addChildToBack(nuChild);
|
||||
else
|
||||
n.addChildBefore(nuChild, nextChild);
|
||||
}
|
||||
}
|
||||
child = nextChild;
|
||||
}
|
||||
}
|
||||
|
||||
private static void buildStatementList_r(Node node, ObjArray statements)
|
||||
{
|
||||
int type = node.getType();
|
||||
if (type == Token.BLOCK
|
||||
|| type == Token.LOCAL_BLOCK
|
||||
|| type == Token.LOOP
|
||||
|| type == Token.FUNCTION)
|
||||
{
|
||||
Node child = node.getFirstChild();
|
||||
while (child != null) {
|
||||
buildStatementList_r(child, statements);
|
||||
child = child.getNext();
|
||||
}
|
||||
} else {
|
||||
statements.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean inDirectCallFunction;
|
||||
OptFunctionNode theFunction;
|
||||
private boolean parameterUsedInNumberContext;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue