summaryrefslogtreecommitdiffstats
path: root/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Node.java
diff options
context:
space:
mode:
Diffstat (limited to 'infrastructure/rhino1_7R1/src/org/mozilla/javascript/Node.java')
-rw-r--r--infrastructure/rhino1_7R1/src/org/mozilla/javascript/Node.java1394
1 files changed, 0 insertions, 1394 deletions
diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Node.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Node.java
deleted file mode 100644
index 4298388..0000000
--- a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/Node.java
+++ /dev/null
@@ -1,1394 +0,0 @@
-/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * ***** 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
- * Roshan James
- * Roger Lawrence
- * Mike McCabe
- *
- * 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;
-
-import java.util.Map;
-import java.util.LinkedHashMap;
-import java.util.Iterator;
-import java.util.Collections;
-
-/**
- * This class implements the root of the intermediate representation.
- *
- * @author Norris Boyd
- * @author Mike McCabe
- */
-
-public class Node
-{
- public static final int
- FUNCTION_PROP = 1,
- LOCAL_PROP = 2,
- LOCAL_BLOCK_PROP = 3,
- REGEXP_PROP = 4,
- CASEARRAY_PROP = 5,
- /*
- the following properties are defined and manipulated by the
- optimizer -
- TARGETBLOCK_PROP - the block referenced by a branch node
- VARIABLE_PROP - the variable referenced by a BIND or NAME node
- ISNUMBER_PROP - this node generates code on Number children and
- delivers a Number result (as opposed to Objects)
- DIRECTCALL_PROP - this call node should emit code to test the function
- object against the known class and call diret if it
- matches.
- */
-
- TARGETBLOCK_PROP = 6,
- VARIABLE_PROP = 7,
- ISNUMBER_PROP = 8,
- DIRECTCALL_PROP = 9,
- SPECIALCALL_PROP = 10,
- SKIP_INDEXES_PROP = 11, // array of skipped indexes of array literal
- OBJECT_IDS_PROP = 12, // array of properties for object literal
- INCRDECR_PROP = 13, // pre or post type of increment/decerement
- CATCH_SCOPE_PROP = 14, // index of catch scope block in catch
- LABEL_ID_PROP = 15, // label id: code generation uses it
- MEMBER_TYPE_PROP = 16, // type of element access operation
- NAME_PROP = 17, // property name
- CONTROL_BLOCK_PROP = 18, // flags a control block that can drop off
- PARENTHESIZED_PROP = 19, // expression is parenthesized
- GENERATOR_END_PROP = 20,
- DESTRUCTURING_ARRAY_LENGTH = 21,
- DESTRUCTURING_NAMES= 22,
- LAST_PROP = 22;
-
- // values of ISNUMBER_PROP to specify
- // which of the children are Number types
- public static final int
- BOTH = 0,
- LEFT = 1,
- RIGHT = 2;
-
- public static final int // values for SPECIALCALL_PROP
- NON_SPECIALCALL = 0,
- SPECIALCALL_EVAL = 1,
- SPECIALCALL_WITH = 2;
-
- public static final int // flags for INCRDECR_PROP
- DECR_FLAG = 0x1,
- POST_FLAG = 0x2;
-
- public static final int // flags for MEMBER_TYPE_PROP
- PROPERTY_FLAG = 0x1, // property access: element is valid name
- ATTRIBUTE_FLAG = 0x2, // x.@y or x..@y
- DESCENDANTS_FLAG = 0x4; // x..y or x..@i
-
- private static class NumberNode extends Node
- {
- NumberNode(double number)
- {
- super(Token.NUMBER);
- this.number = number;
- }
-
- double number;
- }
-
- private static class StringNode extends Node
- {
- StringNode(int type, String str) {
- super(type);
- this.str = str;
- }
-
- String str;
- Node.Scope scope;
- }
-
- public static class Jump extends Node
- {
- public Jump(int type)
- {
- super(type);
- }
-
- Jump(int type, int lineno)
- {
- super(type, lineno);
- }
-
- Jump(int type, Node child)
- {
- super(type, child);
- }
-
- Jump(int type, Node child, int lineno)
- {
- super(type, child, lineno);
- }
-
- public final Jump getJumpStatement()
- {
- if (!(type == Token.BREAK || type == Token.CONTINUE)) Kit.codeBug();
- return jumpNode;
- }
-
- public final void setJumpStatement(Jump jumpStatement)
- {
- if (!(type == Token.BREAK || type == Token.CONTINUE)) Kit.codeBug();
- if (jumpStatement == null) Kit.codeBug();
- if (this.jumpNode != null) Kit.codeBug(); //only once
- this.jumpNode = jumpStatement;
- }
-
- public final Node getDefault()
- {
- if (!(type == Token.SWITCH)) Kit.codeBug();
- return target2;
- }
-
- public final void setDefault(Node defaultTarget)
- {
- if (!(type == Token.SWITCH)) Kit.codeBug();
- if (defaultTarget.type != Token.TARGET) Kit.codeBug();
- if (target2 != null) Kit.codeBug(); //only once
- target2 = defaultTarget;
- }
-
- public final Node getFinally()
- {
- if (!(type == Token.TRY)) Kit.codeBug();
- return target2;
- }
-
- public final void setFinally(Node finallyTarget)
- {
- if (!(type == Token.TRY)) Kit.codeBug();
- if (finallyTarget.type != Token.TARGET) Kit.codeBug();
- if (target2 != null) Kit.codeBug(); //only once
- target2 = finallyTarget;
- }
-
- public final Jump getLoop()
- {
- if (!(type == Token.LABEL)) Kit.codeBug();
- return jumpNode;
- }
-
- public final void setLoop(Jump loop)
- {
- if (!(type == Token.LABEL)) Kit.codeBug();
- if (loop == null) Kit.codeBug();
- if (jumpNode != null) Kit.codeBug(); //only once
- jumpNode = loop;
- }
-
- public final Node getContinue()
- {
- if (type != Token.LOOP) Kit.codeBug();
- return target2;
- }
-
- public final void setContinue(Node continueTarget)
- {
- if (type != Token.LOOP) Kit.codeBug();
- if (continueTarget.type != Token.TARGET) Kit.codeBug();
- if (target2 != null) Kit.codeBug(); //only once
- target2 = continueTarget;
- }
-
- public Node target;
- private Node target2;
- private Jump jumpNode;
- }
-
- static class Symbol {
- Symbol(int declType, String name) {
- this.declType = declType;
- this.name = name;
- this.index = -1;
- }
- /**
- * One of Token.FUNCTION, Token.LP (for parameters), Token.VAR,
- * Token.LET, or Token.CONST
- */
- int declType;
- int index;
- String name;
- Node.Scope containingTable;
- }
-
- static class Scope extends Jump {
- public Scope(int nodeType) {
- super(nodeType);
- }
-
- public Scope(int nodeType, int lineno) {
- super(nodeType, lineno);
- }
-
- public Scope(int nodeType, Node n, int lineno) {
- super(nodeType, n, lineno);
- }
-
- /*
- * Creates a new scope node, moving symbol table information
- * from "scope" to the new node, and making "scope" a nested
- * scope contained by the new node.
- * Useful for injecting a new scope in a scope chain.
- */
- public static Scope splitScope(Scope scope) {
- Scope result = new Scope(scope.getType());
- result.symbolTable = scope.symbolTable;
- scope.symbolTable = null;
- result.parent = scope.parent;
- scope.parent = result;
- result.top = scope.top;
- return result;
- }
-
- public static void joinScopes(Scope source, Scope dest) {
- source.ensureSymbolTable();
- dest.ensureSymbolTable();
- if (!Collections.disjoint(source.symbolTable.keySet(),
- dest.symbolTable.keySet()))
- {
- throw Kit.codeBug();
- }
- dest.symbolTable.putAll(source.symbolTable);
- }
-
- public void setParent(Scope parent) {
- this.parent = parent;
- this.top = parent == null ? (ScriptOrFnNode)this : parent.top;
- }
-
- public Scope getParentScope() {
- return parent;
- }
-
- public Scope getDefiningScope(String name) {
- for (Scope sn=this; sn != null; sn = sn.parent) {
- if (sn.symbolTable == null)
- continue;
- if (sn.symbolTable.containsKey(name))
- return sn;
- }
- return null;
- }
-
- public Symbol getSymbol(String name) {
- return symbolTable == null ? null : symbolTable.get(name);
- }
-
- public void putSymbol(String name, Symbol symbol) {
- ensureSymbolTable();
- symbolTable.put(name, symbol);
- symbol.containingTable = this;
- top.addSymbol(symbol);
- }
-
- public Map<String,Symbol> getSymbolTable() {
- return symbolTable;
- }
-
- private void ensureSymbolTable() {
- if (symbolTable == null) {
- symbolTable = new LinkedHashMap<String,Symbol>(5);
- }
- }
-
- // Use LinkedHashMap so that the iteration order is the insertion order
- protected LinkedHashMap<String,Symbol> symbolTable;
- private Scope parent;
- private ScriptOrFnNode top;
- }
-
- private static class PropListItem
- {
- PropListItem next;
- int type;
- int intValue;
- Object objectValue;
- }
-
-
- public Node(int nodeType) {
- type = nodeType;
- }
-
- public Node(int nodeType, Node child) {
- type = nodeType;
- first = last = child;
- child.next = null;
- }
-
- public Node(int nodeType, Node left, Node right) {
- type = nodeType;
- first = left;
- last = right;
- left.next = right;
- right.next = null;
- }
-
- public Node(int nodeType, Node left, Node mid, Node right) {
- type = nodeType;
- first = left;
- last = right;
- left.next = mid;
- mid.next = right;
- right.next = null;
- }
-
- public Node(int nodeType, int line) {
- type = nodeType;
- lineno = line;
- }
-
- public Node(int nodeType, Node child, int line) {
- this(nodeType, child);
- lineno = line;
- }
-
- public Node(int nodeType, Node left, Node right, int line) {
- this(nodeType, left, right);
- lineno = line;
- }
-
- public Node(int nodeType, Node left, Node mid, Node right, int line) {
- this(nodeType, left, mid, right);
- lineno = line;
- }
-
- public static Node newNumber(double number) {
- return new NumberNode(number);
- }
-
- public static Node newString(String str) {
- return new StringNode(Token.STRING, str);
- }
-
- public static Node newString(int type, String str) {
- return new StringNode(type, str);
- }
-
- public int getType() {
- return type;
- }
-
- public void setType(int type) {
- this.type = type;
- }
-
- public boolean hasChildren() {
- return first != null;
- }
-
- public Node getFirstChild() {
- return first;
- }
-
- public Node getLastChild() {
- return last;
- }
-
- public Node getNext() {
- return next;
- }
-
- public Node getChildBefore(Node child) {
- if (child == first)
- return null;
- Node n = first;
- while (n.next != child) {
- n = n.next;
- if (n == null)
- throw new RuntimeException("node is not a child");
- }
- return n;
- }
-
- public Node getLastSibling() {
- Node n = this;
- while (n.next != null) {
- n = n.next;
- }
- return n;
- }
-
- public void addChildToFront(Node child) {
- child.next = first;
- first = child;
- if (last == null) {
- last = child;
- }
- }
-
- public void addChildToBack(Node child) {
- child.next = null;
- if (last == null) {
- first = last = child;
- return;
- }
- last.next = child;
- last = child;
- }
-
- public void addChildrenToFront(Node children) {
- Node lastSib = children.getLastSibling();
- lastSib.next = first;
- first = children;
- if (last == null) {
- last = lastSib;
- }
- }
-
- public void addChildrenToBack(Node children) {
- if (last != null) {
- last.next = children;
- }
- last = children.getLastSibling();
- if (first == null) {
- first = children;
- }
- }
-
- /**
- * Add 'child' before 'node'.
- */
- public void addChildBefore(Node newChild, Node node) {
- if (newChild.next != null)
- throw new RuntimeException(
- "newChild had siblings in addChildBefore");
- if (first == node) {
- newChild.next = first;
- first = newChild;
- return;
- }
- Node prev = getChildBefore(node);
- addChildAfter(newChild, prev);
- }
-
- /**
- * Add 'child' after 'node'.
- */
- public void addChildAfter(Node newChild, Node node) {
- if (newChild.next != null)
- throw new RuntimeException(
- "newChild had siblings in addChildAfter");
- newChild.next = node.next;
- node.next = newChild;
- if (last == node)
- last = newChild;
- }
-
- public void removeChild(Node child) {
- Node prev = getChildBefore(child);
- if (prev == null)
- first = first.next;
- else
- prev.next = child.next;
- if (child == last) last = prev;
- child.next = null;
- }
-
- public void replaceChild(Node child, Node newChild) {
- newChild.next = child.next;
- if (child == first) {
- first = newChild;
- } else {
- Node prev = getChildBefore(child);
- prev.next = newChild;
- }
- if (child == last)
- last = newChild;
- child.next = null;
- }
-
- public void replaceChildAfter(Node prevChild, Node newChild) {
- Node child = prevChild.next;
- newChild.next = child.next;
- prevChild.next = newChild;
- if (child == last)
- last = newChild;
- child.next = null;
- }
-
- private static final String propToString(int propType)
- {
- if (Token.printTrees) {
- // If Context.printTrees is false, the compiler
- // can remove all these strings.
- switch (propType) {
- case FUNCTION_PROP: return "function";
- case LOCAL_PROP: return "local";
- case LOCAL_BLOCK_PROP: return "local_block";
- case REGEXP_PROP: return "regexp";
- case CASEARRAY_PROP: return "casearray";
-
- case TARGETBLOCK_PROP: return "targetblock";
- case VARIABLE_PROP: return "variable";
- case ISNUMBER_PROP: return "isnumber";
- case DIRECTCALL_PROP: return "directcall";
-
- case SPECIALCALL_PROP: return "specialcall";
- case SKIP_INDEXES_PROP: return "skip_indexes";
- case OBJECT_IDS_PROP: return "object_ids_prop";
- case INCRDECR_PROP: return "incrdecr_prop";
- case CATCH_SCOPE_PROP: return "catch_scope_prop";
- case LABEL_ID_PROP: return "label_id_prop";
- case MEMBER_TYPE_PROP: return "member_type_prop";
- case NAME_PROP: return "name_prop";
- case CONTROL_BLOCK_PROP: return "control_block_prop";
- case PARENTHESIZED_PROP: return "parenthesized_prop";
- case GENERATOR_END_PROP: return "generator_end";
- case DESTRUCTURING_ARRAY_LENGTH:
- return "destructuring_array_length";
- case DESTRUCTURING_NAMES:return "destructuring_names";
-
- default: Kit.codeBug();
- }
- }
- return null;
- }
-
- private PropListItem lookupProperty(int propType)
- {
- PropListItem x = propListHead;
- while (x != null && propType != x.type) {
- x = x.next;
- }
- return x;
- }
-
- private PropListItem ensureProperty(int propType)
- {
- PropListItem item = lookupProperty(propType);
- if (item == null) {
- item = new PropListItem();
- item.type = propType;
- item.next = propListHead;
- propListHead = item;
- }
- return item;
- }
-
- public void removeProp(int propType)
- {
- PropListItem x = propListHead;
- if (x != null) {
- PropListItem prev = null;
- while (x.type != propType) {
- prev = x;
- x = x.next;
- if (x == null) { return; }
- }
- if (prev == null) {
- propListHead = x.next;
- } else {
- prev.next = x.next;
- }
- }
- }
-
- public Object getProp(int propType)
- {
- PropListItem item = lookupProperty(propType);
- if (item == null) { return null; }
- return item.objectValue;
- }
-
- public int getIntProp(int propType, int defaultValue)
- {
- PropListItem item = lookupProperty(propType);
- if (item == null) { return defaultValue; }
- return item.intValue;
- }
-
- public int getExistingIntProp(int propType)
- {
- PropListItem item = lookupProperty(propType);
- if (item == null) { Kit.codeBug(); }
- return item.intValue;
- }
-
- public void putProp(int propType, Object prop)
- {
- if (prop == null) {
- removeProp(propType);
- } else {
- PropListItem item = ensureProperty(propType);
- item.objectValue = prop;
- }
- }
-
- public void putIntProp(int propType, int prop)
- {
- PropListItem item = ensureProperty(propType);
- item.intValue = prop;
- }
-
- public int getLineno() {
- return lineno;
- }
-
- /** Can only be called when <tt>getType() == Token.NUMBER</tt> */
- public final double getDouble() {
- return ((NumberNode)this).number;
- }
-
- public final void setDouble(double number) {
- ((NumberNode)this).number = number;
- }
-
- /** Can only be called when node has String context. */
- public final String getString() {
- return ((StringNode)this).str;
- }
-
- /** Can only be called when node has String context. */
- public final void setString(String s) {
- if (s == null) Kit.codeBug();
- ((StringNode)this).str = s;
- }
-
- /** Can only be called when node has String context. */
- public final Scope getScope() {
- return ((StringNode)this).scope;
- }
-
- /** Can only be called when node has String context. */
- public final void setScope(Scope s) {
- if (s == null) Kit.codeBug();
- if (!(this instanceof StringNode)) {
- throw Kit.codeBug();
- }
- ((StringNode)this).scope = s;
- }
-
- public static Node newTarget()
- {
- return new Node(Token.TARGET);
- }
-
- public final int labelId()
- {
- if (type != Token.TARGET && type != Token.YIELD) Kit.codeBug();
- return getIntProp(LABEL_ID_PROP, -1);
- }
-
- public void labelId(int labelId)
- {
- if (type != Token.TARGET && type != Token.YIELD) Kit.codeBug();
- putIntProp(LABEL_ID_PROP, labelId);
- }
-
-
- /**
- * Does consistent-return analysis on the function body when strict mode is
- * enabled.
- *
- * function (x) { return (x+1) }
- * is ok, but
- * function (x) { if (x < 0) return (x+1); }
- * is not becuase the function can potentially return a value when the
- * condition is satisfied and if not, the function does not explicitly
- * return value.
- *
- * This extends to checking mismatches such as "return" and "return <value>"
- * used in the same function. Warnings are not emitted if inconsistent
- * returns exist in code that can be statically shown to be unreachable.
- * Ex.
- * function (x) { while (true) { ... if (..) { return value } ... } }
- * emits no warning. However if the loop had a break statement, then a
- * warning would be emitted.
- *
- * The consistency analysis looks at control structures such as loops, ifs,
- * switch, try-catch-finally blocks, examines the reachable code paths and
- * warns the user about an inconsistent set of termination possibilities.
- *
- * Caveat: Since the parser flattens many control structures into almost
- * straight-line code with gotos, it makes such analysis hard. Hence this
- * analyser is written to taken advantage of patterns of code generated by
- * the parser (for loops, try blocks and such) and does not do a full
- * control flow analysis of the gotos and break/continue statements.
- * Future changes to the parser will affect this analysis.
- */
-
- /**
- * These flags enumerate the possible ways a statement/function can
- * terminate. These flags are used by endCheck() and by the Parser to
- * detect inconsistent return usage.
- *
- * END_UNREACHED is reserved for code paths that are assumed to always be
- * able to execute (example: throw, continue)
- *
- * END_DROPS_OFF indicates if the statement can transfer control to the
- * next one. Statement such as return dont. A compound statement may have
- * some branch that drops off control to the next statement.
- *
- * END_RETURNS indicates that the statement can return (without arguments)
- * END_RETURNS_VALUE indicates that the statement can return a value.
- *
- * A compound statement such as
- * if (condition) {
- * return value;
- * }
- * Will be detected as (END_DROPS_OFF | END_RETURN_VALUE) by endCheck()
- */
- static final int END_UNREACHED = 0;
- static final int END_DROPS_OFF = 1;
- static final int END_RETURNS = 2;
- static final int END_RETURNS_VALUE = 4;
- static final int END_YIELDS = 8;
-
- /**
- * Checks that every return usage in a function body is consistent with the
- * requirements of strict-mode.
- * @return true if the function satisfies strict mode requirement.
- */
- public boolean hasConsistentReturnUsage()
- {
- int n = endCheck();
- return (n & END_RETURNS_VALUE) == 0 ||
- (n & (END_DROPS_OFF|END_RETURNS|END_YIELDS)) == 0;
- }
-
- /**
- * Returns in the then and else blocks must be consistent with each other.
- * If there is no else block, then the return statement can fall through.
- * @return logical OR of END_* flags
- */
- private int endCheckIf()
- {
- Node th, el;
- int rv = END_UNREACHED;
-
- th = next;
- el = ((Jump)this).target;
-
- rv = th.endCheck();
-
- if (el != null)
- rv |= el.endCheck();
- else
- rv |= END_DROPS_OFF;
-
- return rv;
- }
-
- /**
- * Consistency of return statements is checked between the case statements.
- * If there is no default, then the switch can fall through. If there is a
- * default,we check to see if all code paths in the default return or if
- * there is a code path that can fall through.
- * @return logical OR of END_* flags
- */
- private int endCheckSwitch()
- {
- Node n;
- int rv = END_UNREACHED;
-
- // examine the cases
- for (n = first.next; n != null; n = n.next)
- {
- if (n.type == Token.CASE) {
- rv |= ((Jump)n).target.endCheck();
- } else
- break;
- }
-
- // we don't care how the cases drop into each other
- rv &= ~END_DROPS_OFF;
-
- // examine the default
- n = ((Jump)this).getDefault();
- if (n != null)
- rv |= n.endCheck();
- else
- rv |= END_DROPS_OFF;
-
- // remove the switch block
- rv |= getIntProp(CONTROL_BLOCK_PROP, END_UNREACHED);
-
- return rv;
- }
-
- /**
- * If the block has a finally, return consistency is checked in the
- * finally block. If all code paths in the finally returns, then the
- * returns in the try-catch blocks don't matter. If there is a code path
- * that does not return or if there is no finally block, the returns
- * of the try and catch blocks are checked for mismatch.
- * @return logical OR of END_* flags
- */
- private int endCheckTry()
- {
- Node n;
- int rv = END_UNREACHED;
-
- // check the finally if it exists
- n = ((Jump)this).getFinally();
- if(n != null) {
- rv = n.next.first.endCheck();
- } else {
- rv = END_DROPS_OFF;
- }
-
- // if the finally block always returns, then none of the returns
- // in the try or catch blocks matter
- if ((rv & END_DROPS_OFF) != 0) {
- rv &= ~END_DROPS_OFF;
-
- // examine the try block
- rv |= first.endCheck();
-
- // check each catch block
- n = ((Jump)this).target;
- if (n != null)
- {
- // point to the first catch_scope
- for (n = n.next.first; n != null; n = n.next.next)
- {
- // check the block of user code in the catch_scope
- rv |= n.next.first.next.first.endCheck();
- }
- }
- }
-
- return rv;
- }
-
- /**
- * Return statement in the loop body must be consistent. The default
- * assumption for any kind of a loop is that it will eventually terminate.
- * The only exception is a loop with a constant true condition. Code that
- * follows such a loop is examined only if one can statically determine
- * that there is a break out of the loop.
- * for(<> ; <>; <>) {}
- * for(<> in <> ) {}
- * while(<>) { }
- * do { } while(<>)
- * @return logical OR of END_* flags
- */
- private int endCheckLoop()
- {
- Node n;
- int rv = END_UNREACHED;
-
- // To find the loop body, we look at the second to last node of the
- // loop node, which should be the predicate that the loop should
- // satisfy.
- // The target of the predicate is the loop-body for all 4 kinds of
- // loops.
- for (n = first; n.next != last; n = n.next) {
- /* skip */
- }
- if (n.type != Token.IFEQ)
- return END_DROPS_OFF;
-
- // The target's next is the loop body block
- rv = ((Jump)n).target.next.endCheck();
-
- // check to see if the loop condition is true
- if (n.first.type == Token.TRUE)
- rv &= ~END_DROPS_OFF;
-
- // look for effect of breaks
- rv |= getIntProp(CONTROL_BLOCK_PROP, END_UNREACHED);
-
- return rv;
- }
-
-
- /**
- * A general block of code is examined statement by statement. If any
- * statement (even compound ones) returns in all branches, then subsequent
- * statements are not examined.
- * @return logical OR of END_* flags
- */
- private int endCheckBlock()
- {
- Node n;
- int rv = END_DROPS_OFF;
-
- // check each statment and if the statement can continue onto the next
- // one, then check the next statement
- for (n=first; ((rv & END_DROPS_OFF) != 0) && n != null; n = n.next)
- {
- rv &= ~END_DROPS_OFF;
- rv |= n.endCheck();
- }
- return rv;
- }
-
- /**
- * A labelled statement implies that there maybe a break to the label. The
- * function processes the labelled statement and then checks the
- * CONTROL_BLOCK_PROP property to see if there is ever a break to the
- * particular label.
- * @return logical OR of END_* flags
- */
- private int endCheckLabel()
- {
- int rv = END_UNREACHED;
-
- rv = next.endCheck();
- rv |= getIntProp(CONTROL_BLOCK_PROP, END_UNREACHED);
-
- return rv;
- }
-
- /**
- * When a break is encountered annotate the statement being broken
- * out of by setting its CONTROL_BLOCK_PROP property.
- * @return logical OR of END_* flags
- */
- private int endCheckBreak()
- {
- Node n = ((Jump) this).jumpNode;
- n.putIntProp(CONTROL_BLOCK_PROP, END_DROPS_OFF);
- return END_UNREACHED;
- }
-
- /**
- * endCheck() examines the body of a function, doing a basic reachability
- * analysis and returns a combination of flags END_* flags that indicate
- * how the function execution can terminate. These constitute only the
- * pessimistic set of termination conditions. It is possible that at
- * runtime certain code paths will never be actually taken. Hence this
- * analysis will flag errors in cases where there may not be errors.
- * @return logical OR of END_* flags
- */
- private int endCheck()
- {
- switch(type)
- {
- case Token.BREAK:
- return endCheckBreak();
-
- case Token.EXPR_VOID:
- if (this.first != null)
- return first.endCheck();
- return END_DROPS_OFF;
-
- case Token.YIELD:
- return END_YIELDS;
-
- case Token.CONTINUE:
- case Token.THROW:
- return END_UNREACHED;
-
- case Token.RETURN:
- if (this.first != null)
- return END_RETURNS_VALUE;
- else
- return END_RETURNS;
-
- case Token.TARGET:
- if (next != null)
- return next.endCheck();
- else
- return END_DROPS_OFF;
-
- case Token.LOOP:
- return endCheckLoop();
-
- case Token.LOCAL_BLOCK:
- case Token.BLOCK:
- // there are several special kinds of blocks
- if (first == null)
- return END_DROPS_OFF;
-
- switch(first.type) {
- case Token.LABEL:
- return first.endCheckLabel();
-
- case Token.IFNE:
- return first.endCheckIf();
-
- case Token.SWITCH:
- return first.endCheckSwitch();
-
- case Token.TRY:
- return first.endCheckTry();
-
- default:
- return endCheckBlock();
- }
-
- default:
- return END_DROPS_OFF;
- }
- }
-
- public boolean hasSideEffects()
- {
- switch (type) {
- case Token.EXPR_VOID:
- case Token.COMMA:
- if (last != null)
- return last.hasSideEffects();
- else
- return true;
-
- case Token.HOOK:
- if (first == null ||
- first.next == null ||
- first.next.next == null)
- Kit.codeBug();
- return first.next.hasSideEffects() &&
- first.next.next.hasSideEffects();
-
- case Token.ERROR: // Avoid cascaded error messages
- case Token.EXPR_RESULT:
- case Token.ASSIGN:
- case Token.ASSIGN_ADD:
- case Token.ASSIGN_SUB:
- case Token.ASSIGN_MUL:
- case Token.ASSIGN_DIV:
- case Token.ASSIGN_MOD:
- case Token.ASSIGN_BITOR:
- case Token.ASSIGN_BITXOR:
- case Token.ASSIGN_BITAND:
- case Token.ASSIGN_LSH:
- case Token.ASSIGN_RSH:
- case Token.ASSIGN_URSH:
- case Token.ENTERWITH:
- case Token.LEAVEWITH:
- case Token.RETURN:
- case Token.GOTO:
- case Token.IFEQ:
- case Token.IFNE:
- case Token.NEW:
- case Token.DELPROP:
- case Token.SETNAME:
- case Token.SETPROP:
- case Token.SETELEM:
- case Token.CALL:
- case Token.THROW:
- case Token.RETHROW:
- case Token.SETVAR:
- case Token.CATCH_SCOPE:
- case Token.RETURN_RESULT:
- case Token.SET_REF:
- case Token.DEL_REF:
- case Token.REF_CALL:
- case Token.TRY:
- case Token.SEMI:
- case Token.INC:
- case Token.DEC:
- case Token.EXPORT:
- case Token.IMPORT:
- case Token.IF:
- case Token.ELSE:
- case Token.SWITCH:
- case Token.WHILE:
- case Token.DO:
- case Token.FOR:
- case Token.BREAK:
- case Token.CONTINUE:
- case Token.VAR:
- case Token.CONST:
- case Token.LET:
- case Token.LETEXPR:
- case Token.WITH:
- case Token.WITHEXPR:
- case Token.CATCH:
- case Token.FINALLY:
- case Token.BLOCK:
- case Token.LABEL:
- case Token.TARGET:
- case Token.LOOP:
- case Token.JSR:
- case Token.SETPROP_OP:
- case Token.SETELEM_OP:
- case Token.LOCAL_BLOCK:
- case Token.SET_REF_OP:
- case Token.YIELD:
- return true;
-
- default:
- return false;
- }
- }
-
- public String toString()
- {
- if (Token.printTrees) {
- StringBuffer sb = new StringBuffer();
- toString(new ObjToIntMap(), sb);
- return sb.toString();
- }
- return String.valueOf(type);
- }
-
- private void toString(ObjToIntMap printIds, StringBuffer sb)
- {
- if (Token.printTrees) {
- sb.append(Token.name(type));
- if (this instanceof StringNode) {
- sb.append(' ');
- sb.append(getString());
- Scope scope = getScope();
- if (scope != null) {
- sb.append("[scope: ");
- appendPrintId(scope, printIds, sb);
- sb.append("]");
- }
- } else if (this instanceof Node.Scope) {
- if (this instanceof ScriptOrFnNode) {
- ScriptOrFnNode sof = (ScriptOrFnNode)this;
- if (this instanceof FunctionNode) {
- FunctionNode fn = (FunctionNode)this;
- sb.append(' ');
- sb.append(fn.getFunctionName());
- }
- sb.append(" [source name: ");
- sb.append(sof.getSourceName());
- sb.append("] [encoded source length: ");
- sb.append(sof.getEncodedSourceEnd()
- - sof.getEncodedSourceStart());
- sb.append("] [base line: ");
- sb.append(sof.getBaseLineno());
- sb.append("] [end line: ");
- sb.append(sof.getEndLineno());
- sb.append(']');
- }
- if (((Node.Scope)this).symbolTable != null) {
- sb.append(" [scope ");
- appendPrintId(this, printIds, sb);
- sb.append(": ");
- Iterator iter = ((Node.Scope) this).symbolTable.keySet()
- .iterator();
- while (iter.hasNext()) {
- sb.append(iter.next());
- sb.append(" ");
- }
- sb.append("]");
- }
- } else if (this instanceof Jump) {
- Jump jump = (Jump)this;
- if (type == Token.BREAK || type == Token.CONTINUE) {
- sb.append(" [label: ");
- appendPrintId(jump.getJumpStatement(), printIds, sb);
- sb.append(']');
- } else if (type == Token.TRY) {
- Node catchNode = jump.target;
- Node finallyTarget = jump.getFinally();
- if (catchNode != null) {
- sb.append(" [catch: ");
- appendPrintId(catchNode, printIds, sb);
- sb.append(']');
- }
- if (finallyTarget != null) {
- sb.append(" [finally: ");
- appendPrintId(finallyTarget, printIds, sb);
- sb.append(']');
- }
- } else if (type == Token.LABEL || type == Token.LOOP
- || type == Token.SWITCH)
- {
- sb.append(" [break: ");
- appendPrintId(jump.target, printIds, sb);
- sb.append(']');
- if (type == Token.LOOP) {
- sb.append(" [continue: ");
- appendPrintId(jump.getContinue(), printIds, sb);
- sb.append(']');
- }
- } else {
- sb.append(" [target: ");
- appendPrintId(jump.target, printIds, sb);
- sb.append(']');
- }
- } else if (type == Token.NUMBER) {
- sb.append(' ');
- sb.append(getDouble());
- } else if (type == Token.TARGET) {
- sb.append(' ');
- appendPrintId(this, printIds, sb);
- }
- if (lineno != -1) {
- sb.append(' ');
- sb.append(lineno);
- }
-
- for (PropListItem x = propListHead; x != null; x = x.next) {
- int type = x.type;
- sb.append(" [");
- sb.append(propToString(type));
- sb.append(": ");
- String value;
- switch (type) {
- case TARGETBLOCK_PROP : // can't add this as it recurses
- value = "target block property";
- break;
- case LOCAL_BLOCK_PROP : // can't add this as it is dull
- value = "last local block";
- break;
- case ISNUMBER_PROP:
- switch (x.intValue) {
- case BOTH:
- value = "both";
- break;
- case RIGHT:
- value = "right";
- break;
- case LEFT:
- value = "left";
- break;
- default:
- throw Kit.codeBug();
- }
- break;
- case SPECIALCALL_PROP:
- switch (x.intValue) {
- case SPECIALCALL_EVAL:
- value = "eval";
- break;
- case SPECIALCALL_WITH:
- value = "with";
- break;
- default:
- // NON_SPECIALCALL should not be stored
- throw Kit.codeBug();
- }
- break;
- case OBJECT_IDS_PROP: {
- Object[] a = (Object[]) x.objectValue;
- value = "[";
- for (int i=0; i < a.length; i++) {
- value += a[i].toString();
- if (i+1 < a.length)
- value += ", ";
- }
- value += "]";
- break;
- }
- default :
- Object obj = x.objectValue;
- if (obj != null) {
- value = obj.toString();
- } else {
- value = String.valueOf(x.intValue);
- }
- break;
- }
- sb.append(value);
- sb.append(']');
- }
- }
- }
-
- public String toStringTree(ScriptOrFnNode treeTop) {
- if (Token.printTrees) {
- StringBuffer sb = new StringBuffer();
- toStringTreeHelper(treeTop, this, null, 0, sb);
- return sb.toString();
- }
- return null;
- }
-
- private static void toStringTreeHelper(ScriptOrFnNode treeTop, Node n,
- ObjToIntMap printIds,
- int level, StringBuffer sb)
- {
- if (Token.printTrees) {
- if (printIds == null) {
- printIds = new ObjToIntMap();
- generatePrintIds(treeTop, printIds);
- }
- for (int i = 0; i != level; ++i) {
- sb.append(" ");
- }
- n.toString(printIds, sb);
- sb.append('\n');
- for (Node cursor = n.getFirstChild(); cursor != null;
- cursor = cursor.getNext())
- {
- if (cursor.getType() == Token.FUNCTION) {
- int fnIndex = cursor.getExistingIntProp(Node.FUNCTION_PROP);
- FunctionNode fn = treeTop.getFunctionNode(fnIndex);
- toStringTreeHelper(fn, fn, null, level + 1, sb);
- } else {
- toStringTreeHelper(treeTop, cursor, printIds, level + 1, sb);
- }
- }
- }
- }
-
- private static void generatePrintIds(Node n, ObjToIntMap map)
- {
- if (Token.printTrees) {
- map.put(n, map.size());
- for (Node cursor = n.getFirstChild(); cursor != null;
- cursor = cursor.getNext())
- {
- generatePrintIds(cursor, map);
- }
- }
- }
-
- private static void appendPrintId(Node n, ObjToIntMap printIds,
- StringBuffer sb)
- {
- if (Token.printTrees) {
- if (n != null) {
- int id = printIds.get(n, -1);
- sb.append('#');
- if (id != -1) {
- sb.append(id + 1);
- } else {
- sb.append("<not_available>");
- }
- }
- }
- }
-
- int type; // type of the node; Token.NAME for example
- Node next; // next sibling
- private Node first; // first element of a linked list of children
- private Node last; // last element of a linked list of children
- protected int lineno = -1;
-
- /*APPJET*/public int statementEndLineNum = -1;
-
- /**
- * Linked list of properties. Since vast majority of nodes would have
- * no more then 2 properties, linked list saves memory and provides
- * fast lookup. If this does not holds, propListHead can be replaced
- * by UintMap.
- */
- private PropListItem propListHead;
-}