📄 node.java
字号:
} /** * 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; /** * 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;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -