From 0470e048abb345f567d5fda1e9564715a9f4d5d7 Mon Sep 17 00:00:00 2001 From: "nboyd%atg.com" Date: Mon, 30 Oct 2000 19:34:11 +0000 Subject: [PATCH] Fix bugs: 56318 function literals with names don't work right 57045 negative integers as object properties: weird behavior 58479 functions defined within conditional phrases are always crea --- .../mozilla/classfile/ClassFileWriter.java | 3 +- .../org/mozilla/javascript/Interpreter.java | 8 +- .../mozilla/javascript/InterpreterData.java | 2 +- .../mozilla/javascript/NodeTransformer.java | 32 ++++- .../org/mozilla/javascript/ScriptRuntime.java | 22 ++- .../org/mozilla/javascript/TokenStream.java | 126 +++++++++--------- .../javascript/debug/DebuggableEngine.java | 2 + .../javascript/debug/DebuggableScript.java | 2 + .../mozilla/javascript/optimizer/Block.java | 12 +- .../mozilla/javascript/optimizer/Codegen.java | 30 +++-- .../javascript/optimizer/Optimizer.java | 6 +- .../mozilla/classfile/ClassFileWriter.java | 3 +- .../org/mozilla/javascript/Interpreter.java | 8 +- .../mozilla/javascript/InterpreterData.java | 2 +- .../mozilla/javascript/NodeTransformer.java | 32 ++++- .../org/mozilla/javascript/ScriptRuntime.java | 22 ++- .../org/mozilla/javascript/TokenStream.java | 126 +++++++++--------- .../javascript/debug/DebuggableEngine.java | 2 + .../javascript/debug/DebuggableScript.java | 2 + .../mozilla/javascript/optimizer/Block.java | 12 +- .../mozilla/javascript/optimizer/Codegen.java | 30 +++-- .../javascript/optimizer/Optimizer.java | 6 +- 22 files changed, 300 insertions(+), 190 deletions(-) diff --git a/js/rhino/org/mozilla/classfile/ClassFileWriter.java b/js/rhino/org/mozilla/classfile/ClassFileWriter.java index 5273ee830644..c7c582f8214c 100644 --- a/js/rhino/org/mozilla/classfile/ClassFileWriter.java +++ b/js/rhino/org/mozilla/classfile/ClassFileWriter.java @@ -362,7 +362,8 @@ public class ClassFileWriter extends LabelTable { codeAttribute[index++] = (byte)(varCount >> 8); codeAttribute[index++] = (byte)varCount; for (int i = 0; i < varCount; i++) { - LocalVariable lvar = vars.get(i); + LocalVariable lvar = vars.getVariable(i); + // start pc int startPc = lvar.getStartPC(); codeAttribute[index++] = (byte)(startPc >> 8); diff --git a/js/rhino/org/mozilla/javascript/Interpreter.java b/js/rhino/org/mozilla/javascript/Interpreter.java index 17ac1928fc62..fbaf5ea70ccb 100644 --- a/js/rhino/org/mozilla/javascript/Interpreter.java +++ b/js/rhino/org/mozilla/javascript/Interpreter.java @@ -174,8 +174,8 @@ public class Interpreter extends LabelTable { boolean needsActivation = theFunction.requiresActivation() || cx.isGeneratingDebug(); generateICodeFromTree(theFunction.getLastChild(), - varTable, needsActivation, - securityDomain); + varTable, needsActivation, + securityDomain); itsData.itsName = theFunction.getFunctionName(); itsData.itsSourceFile = (String) theFunction.getProp( @@ -1204,6 +1204,7 @@ public class Interpreter extends LabelTable { case TokenStream.ONE : case TokenStream.NULL : case TokenStream.THIS : + case TokenStream.THISFN : case TokenStream.FALSE : case TokenStream.TRUE : case TokenStream.UNDEFINED : @@ -1802,6 +1803,9 @@ public class Interpreter extends LabelTable { case TokenStream.THIS : stack[++stackTop] = thisObj; break; + case TokenStream.THISFN : + stack[++stackTop] = fnOrScript; + break; case TokenStream.FALSE : stack[++stackTop] = Boolean.FALSE; break; diff --git a/js/rhino/org/mozilla/javascript/InterpreterData.java b/js/rhino/org/mozilla/javascript/InterpreterData.java index 58072fa8df65..60b575b5a11e 100644 --- a/js/rhino/org/mozilla/javascript/InterpreterData.java +++ b/js/rhino/org/mozilla/javascript/InterpreterData.java @@ -80,7 +80,7 @@ class InterpreterData { public boolean removeBreakpoint(int line) { int offset = getOffset(line); - if (offset != -1 && itsICode[offset] == TokenStream.BREAKPOINT) + if (offset != -1 && itsICode[offset] == (byte) TokenStream.BREAKPOINT) { itsICode[offset] = (byte) TokenStream.LINE; return true; diff --git a/js/rhino/org/mozilla/javascript/NodeTransformer.java b/js/rhino/org/mozilla/javascript/NodeTransformer.java index ec80e53478e4..9758f9c90ba8 100644 --- a/js/rhino/org/mozilla/javascript/NodeTransformer.java +++ b/js/rhino/org/mozilla/javascript/NodeTransformer.java @@ -449,7 +449,7 @@ public class NodeTransformer { ((FunctionNode) tree).setRequiresActivation(true); } VariableTable vars = getVariableTable(tree); - if (vars.get(name) != null) { + if (vars.getVariable(name) != null) { if (type == TokenStream.SETNAME) { node.setType(TokenStream.SETVAR); bind.setType(TokenStream.STRING); @@ -489,7 +489,7 @@ public class NodeTransformer { ((FunctionNode) tree).setRequiresActivation(true); } VariableTable vars = getVariableTable(tree); - if (vars.get(name) != null) { + if (vars.getVariable(name) != null) { node.setType(TokenStream.GETVAR); } break; @@ -510,7 +510,9 @@ public class NodeTransformer { while ((node = iterator.nextNode()) != null) { int nodeType = node.getType(); if (inFunction && nodeType == TokenStream.FUNCTION && - node != tree) + node != tree && + ((FunctionNode) node.getProp(Node.FUNCTION_PROP)).getFunctionType() == + FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { // In a function with both "var x" and "function x", // disregard the var statement, independent of order. @@ -531,6 +533,26 @@ public class NodeTransformer { vars.addLocal(n.getString()); } } + String name = (String) tree.getDatum(); + if (inFunction && ((FunctionNode) tree).getFunctionType() == + FunctionNode.FUNCTION_EXPRESSION && + name != null && name.length() > 0 && + vars.getVariable(name) == null) + { + // A function expression needs to have its name as a variable + // (if it isn't already allocated as a variable). See + // ECMA Ch. 13. We add code to the beginning of the function + // to initialize a local variable of the function's name + // to the function value. + vars.addLocal(name); + Node block = tree.getLastChild(); + Node setFn = new Node(TokenStream.POP, + new Node(TokenStream.SETVAR, + new Node(TokenStream.STRING, name), + new Node(TokenStream.PRIMARY, + new Integer(TokenStream.THISFN)))); + block.addChildrenToFront(setFn); + } } protected void addParameters(FunctionNode fnNode) { @@ -580,7 +602,9 @@ public class NodeTransformer { if (left.getType() == TokenStream.NAME) { VariableTable vars = getVariableTable(tree); String name = left.getString(); - if (inFunction && vars.get(name) != null && !inWithStatement()) { + if (inFunction && vars.getVariable(name) != null && + !inWithStatement()) + { // call to a var. Transform to Call(GetVar("a"), b, c) left.setType(TokenStream.GETVAR); // fall through to code to add GetParent diff --git a/js/rhino/org/mozilla/javascript/ScriptRuntime.java b/js/rhino/org/mozilla/javascript/ScriptRuntime.java index a70ef11b0c41..f5af4e6a1dcf 100644 --- a/js/rhino/org/mozilla/javascript/ScriptRuntime.java +++ b/js/rhino/org/mozilla/javascript/ScriptRuntime.java @@ -854,12 +854,18 @@ public class ScriptRuntime { /* It must be a string. */ int len = str.length(); char c; - if (len > 0 && '0' <= (c = str.charAt(0)) && c <= '9' && + boolean negate = false; + int i = 0; + if (len > 0 && (c = str.charAt(0)) == '-') { + negate = true; + i = 1; + } + if (len > 0 && '0' <= (c = str.charAt(i)) && c <= '9' && len <= MAX_VALUE_LENGTH) { int index = c - '0'; int oldIndex = 0; - int i = 1; + i++; if (index != 0) { while (i < len && '0' <= (c = str.charAt(i)) && c <= '9') { oldIndex = index; @@ -875,7 +881,7 @@ public class ScriptRuntime { (oldIndex == (Integer.MAX_VALUE / 10) && c < (Integer.MAX_VALUE % 10)))) { - return index; + return negate ? -index : index; } } return NO_INDEX; @@ -1159,9 +1165,14 @@ public class ScriptRuntime { } while (next != null); bound.put(id, bound, value); + /* + This code is causing immense performance problems in + scripts that assign to the variables as a way of creating them. + XXX need strict mode String[] args = { id }; String message = getMessage("msg.assn.create", args); Context.reportWarning(message); + */ return value; } return setProp(bound, id, value, scope); @@ -1947,11 +1958,12 @@ public class ScriptRuntime { public static NativeFunction initFunction(NativeFunction fn, Scriptable scope, String fnName, - Context cx) + Context cx, + boolean doSetName) { fn.setPrototype(ScriptableObject.getClassPrototype(scope, "Function")); fn.setParentScope(scope); - if (fnName != null && fnName.length() != 0) + if (doSetName) setName(scope, fn, scope, fnName); return fn; } diff --git a/js/rhino/org/mozilla/javascript/TokenStream.java b/js/rhino/org/mozilla/javascript/TokenStream.java index a4d2bd1762e1..cf7eee3d89f1 100644 --- a/js/rhino/org/mozilla/javascript/TokenStream.java +++ b/js/rhino/org/mozilla/javascript/TokenStream.java @@ -168,47 +168,48 @@ public class TokenStream { SETPARENT = 84, SCOPE = 85, GETSCOPEPARENT = 86, - JTHROW = 87, + THISFN = 87, + JTHROW = 88, // End of interpreter bytecodes - SEMI = 88, // semicolon - LB = 89, // left and right brackets - RB = 90, - LC = 91, // left and right curlies (braces) - RC = 92, - LP = 93, // left and right parentheses - RP = 94, - COMMA = 95, // comma operator - ASSIGN = 96, // assignment ops (= += -= etc.) - HOOK = 97, // conditional (?:) - COLON = 98, - OR = 99, // logical or (||) - AND = 100, // logical and (&&) - EQOP = 101, // equality ops (== !=) - RELOP = 102, // relational ops (< <= > >=) - SHOP = 103, // shift ops (<< >> >>>) - UNARYOP = 104, // unary prefix operator - INC = 105, // increment/decrement (++ --) - DEC = 106, - DOT = 107, // member operator (.) - PRIMARY = 108, // true, false, null, this - FUNCTION = 109, // function keyword - EXPORT = 110, // export keyword - IMPORT = 111, // import keyword - IF = 112, // if keyword - ELSE = 113, // else keyword - SWITCH = 114, // switch keyword - CASE = 115, // case keyword - DEFAULT = 116, // default keyword - WHILE = 117, // while keyword - DO = 118, // do keyword - FOR = 119, // for keyword - BREAK = 120, // break keyword - CONTINUE = 121, // continue keyword - VAR = 122, // var keyword - WITH = 123, // with keyword - CATCH = 124, // catch keyword - FINALLY = 125, // finally keyword - RESERVED = 126, // reserved keywords + SEMI = 89, // semicolon + LB = 90, // left and right brackets + RB = 91, + LC = 92, // left and right curlies (braces) + RC = 93, + LP = 94, // left and right parentheses + RP = 95, + COMMA = 96, // comma operator + ASSIGN = 97, // assignment ops (= += -= etc.) + HOOK = 98, // conditional (?:) + COLON = 99, + OR = 100, // logical or (||) + AND = 101, // logical and (&&) + EQOP = 102, // equality ops (== !=) + RELOP = 103, // relational ops (< <= > >=) + SHOP = 104, // shift ops (<< >> >>>) + UNARYOP = 105, // unary prefix operator + INC = 106, // increment/decrement (++ --) + DEC = 107, + DOT = 108, // member operator (.) + PRIMARY = 109, // true, false, null, this + FUNCTION = 110, // function keyword + EXPORT = 111, // export keyword + IMPORT = 112, // import keyword + IF = 113, // if keyword + ELSE = 114, // else keyword + SWITCH = 115, // switch keyword + CASE = 116, // case keyword + DEFAULT = 117, // default keyword + WHILE = 118, // while keyword + DO = 119, // do keyword + FOR = 120, // for keyword + BREAK = 121, // break keyword + CONTINUE = 122, // continue keyword + VAR = 123, // var keyword + WITH = 124, // with keyword + CATCH = 125, // catch keyword + FINALLY = 126, // finally keyword + RESERVED = 127, // reserved keywords /** Added by Mike - these are JSOPs in the jsref, but I * don't have them yet in the java implementation... @@ -217,45 +218,45 @@ public class TokenStream { * Most of these go in the 'op' field when returning * more general token types, eg. 'DIV' as the op of 'ASSIGN'. */ - NOP = 127, // NOP - NOT = 128, // etc. - PRE = 129, // for INC, DEC nodes. - POST = 130, + NOP = 128, // NOP + NOT = 129, // etc. + PRE = 130, // for INC, DEC nodes. + POST = 131, /** * For JSOPs associated with keywords... * eg. op = THIS; token = PRIMARY */ - VOID = 131, + VOID = 132, /* types used for the parse tree - these never get returned * by the scanner. */ - BLOCK = 132, // statement block - ARRAYLIT = 133, // array literal - OBJLIT = 134, // object literal - LABEL = 135, // label - TARGET = 136, - LOOP = 137, - ENUMDONE = 138, - EXPRSTMT = 139, - PARENT = 140, - CONVERT = 141, - JSR = 142, - NEWLOCAL = 143, - USELOCAL = 144, - SCRIPT = 145, // top-level node for entire script + BLOCK = 133, // statement block + ARRAYLIT = 134, // array literal + OBJLIT = 135, // object literal + LABEL = 136, // label + TARGET = 137, + LOOP = 138, + ENUMDONE = 139, + EXPRSTMT = 140, + PARENT = 141, + CONVERT = 142, + JSR = 143, + NEWLOCAL = 144, + USELOCAL = 145, + SCRIPT = 146, // top-level node for entire script /** * For the interpreted mode indicating a line number change in icodes. */ - LINE = 146, - SOURCEFILE = 147, + LINE = 147, + SOURCEFILE = 148, // For debugger - BREAKPOINT = 148; + BREAKPOINT = 149; // end enum @@ -354,6 +355,7 @@ public class TokenStream { "setparent", "scope", "getscopeparent", + "thisfn", "jthrow", "semi", "lb", diff --git a/js/rhino/org/mozilla/javascript/debug/DebuggableEngine.java b/js/rhino/org/mozilla/javascript/debug/DebuggableEngine.java index b67c82ddfa32..dc066bd412b3 100644 --- a/js/rhino/org/mozilla/javascript/debug/DebuggableEngine.java +++ b/js/rhino/org/mozilla/javascript/debug/DebuggableEngine.java @@ -49,4 +49,6 @@ public interface DebuggableEngine { public Debugger getDebugger(); public Frame getFrame(int frameNumber); + + //public void haltExecution(); } diff --git a/js/rhino/org/mozilla/javascript/debug/DebuggableScript.java b/js/rhino/org/mozilla/javascript/debug/DebuggableScript.java index 835ccb3f26aa..db86b6973185 100644 --- a/js/rhino/org/mozilla/javascript/debug/DebuggableScript.java +++ b/js/rhino/org/mozilla/javascript/debug/DebuggableScript.java @@ -46,6 +46,8 @@ import java.util.Enumeration; * code (either functions or top-level scripts). */ public interface DebuggableScript { + + //public boolean isFunction(); // XXX /** * Get the Scriptable object (Function or Script) that is diff --git a/js/rhino/org/mozilla/javascript/optimizer/Block.java b/js/rhino/org/mozilla/javascript/optimizer/Block.java index 591306786ba0..a37866d87cd9 100644 --- a/js/rhino/org/mozilla/javascript/optimizer/Block.java +++ b/js/rhino/org/mozilla/javascript/optimizer/Block.java @@ -217,7 +217,7 @@ public class Block { } for (int i = 0; i < liveSet.length; i++) { if (liveSet[i]) - ((OptLocalVariable)theVariables.get(i)).markLiveAcrossCall(); + ((OptLocalVariable)theVariables.getVariable(i)).markLiveAcrossCall(); } } break; @@ -246,7 +246,7 @@ public class Block { { for (int i = 0; i < theVariables.size(); i++) if (itsLiveOnEntrySet.test(i)) - ((OptLocalVariable)theVariables.get(i)).assignType(TypeEvent.AnyType); + ((OptLocalVariable)theVariables.getVariable(i)).assignType(TypeEvent.AnyType); } @@ -650,13 +650,13 @@ public class Block { { for (int i = 0; i < theVariables.size(); i++) { if (itsUseBeforeDefSet.test(i)) - pw.println(theVariables.get(i).getName() + " is used before def'd"); + pw.println(theVariables.getVariable(i).getName() + " is used before def'd"); if (itsNotDefSet.test(i)) - pw.println(theVariables.get(i).getName() + " is not def'd"); + pw.println(theVariables.getVariable(i).getName() + " is not def'd"); if (itsLiveOnEntrySet.test(i)) - pw.println(theVariables.get(i).getName() + " is live on entry"); + pw.println(theVariables.getVariable(i).getName() + " is live on entry"); if (itsLiveOnExitSet.test(i)) - pw.println(theVariables.get(i).getName() + " is live on exit"); + pw.println(theVariables.getVariable(i).getName() + " is live on exit"); } } diff --git a/js/rhino/org/mozilla/javascript/optimizer/Codegen.java b/js/rhino/org/mozilla/javascript/optimizer/Codegen.java index aab4d5f6a413..9c3e10152d5b 100644 --- a/js/rhino/org/mozilla/javascript/optimizer/Codegen.java +++ b/js/rhino/org/mozilla/javascript/optimizer/Codegen.java @@ -457,7 +457,7 @@ public class Codegen extends Interpreter { // make sure that all parameters are objects itsForcedObjectParameters = true; for (int i = 0; i < vars.getParameterCount(); i++) { - OptLocalVariable lVar = (OptLocalVariable) vars.get(i); + OptLocalVariable lVar = (OptLocalVariable) vars.getVariable(i); aload(lVar.getJRegister()); classFile.add(ByteCode.GETSTATIC, "java/lang/Void", @@ -1347,12 +1347,18 @@ public class Codegen extends Interpreter { addByteCode(ByteCode.ACONST_NULL); } // load 'cx' - aload(contextLocal); + aload(contextLocal); + // load boolean indicating whether fn name should be set in scope + boolean setFnName = str != null && str.length() > 0 && + ((FunctionNode) def).getFunctionType() != + FunctionNode.FUNCTION_EXPRESSION; + addByteCode(setFnName ? ByteCode.ICONST_1 : ByteCode.ICONST_0); + addScriptRuntimeInvoke("initFunction", "(Lorg/mozilla/javascript/NativeFunction;" + "Lorg/mozilla/javascript/Scriptable;" + "Ljava/lang/String;" + - "Lorg/mozilla/javascript/Context;)", + "Lorg/mozilla/javascript/Context;Z)", "Lorg/mozilla/javascript/NativeFunction;"); def.putProp(Node.FUNCTION_PROP, new Short(i)); addByteCode(ByteCode.AASTORE); // store NativeFunction @@ -1447,7 +1453,7 @@ public class Codegen extends Interpreter { // before the next call and are used in the function short firstUndefVar = -1; for (int i = 0; i < vars.size(); i++) { - OptLocalVariable lVar = (OptLocalVariable) vars.get(i); + OptLocalVariable lVar = (OptLocalVariable) vars.getVariable(i); if (lVar.isNumber()) { lVar.assignJRegister(getNewWordPairLocal()); push(0.0); @@ -1547,7 +1553,7 @@ public class Codegen extends Interpreter { if (cx.isGeneratingDebug()) { debugVars = new OptVariableTable(); debugVars.addLocal(debugVariableName); - OptLocalVariable lv = (OptLocalVariable) debugVars.get(debugVariableName); + OptLocalVariable lv = (OptLocalVariable) debugVars.getVariable(debugVariableName); lv.assignJRegister(variableObjectLocal); lv.setStartPC(classFile.getCurrentCodeOffset()); } @@ -1876,7 +1882,6 @@ public class Codegen extends Interpreter { */ { -if (true) { Node callBase = callNode.getFirstChild(); if (callBase.getType() == TokenStream.GETPROP) { Node callBaseChild = callBase.getFirstChild(); @@ -1903,7 +1908,6 @@ if (true) { } } } -} return null; } @@ -2425,7 +2429,7 @@ if (true) { } String name = node.getString(); if (hasVarsInRegs) { - OptLocalVariable lVar = (OptLocalVariable) vars.get(name); + OptLocalVariable lVar = (OptLocalVariable) vars.getVariable(name); if (lVar != null) { if (lVar.isNumber()) { push("number"); @@ -2463,7 +2467,7 @@ if (true) { String routine = (isInc) ? "postIncrement" : "postDecrement"; if (hasVarsInRegs && child.getType() == TokenStream.GETVAR) { if (lVar == null) - lVar = (OptLocalVariable) vars.get(child.getString()); + lVar = (OptLocalVariable) vars.getVariable(child.getString()); if (lVar.getJRegister() == -1) lVar.assignJRegister(getNewWordLocal()); aload(lVar.getJRegister()); @@ -3173,6 +3177,10 @@ if (true) { aload(thisObjLocal); break; + case TokenStream.THISFN: + classFile.add(ByteCode.ALOAD_0); + break; + case TokenStream.NULL: addByteCode(ByteCode.ACONST_NULL); break; @@ -3232,7 +3240,7 @@ if (true) { { // TODO: Clean up use of lVar here and in set. if (hasVarsInRegs && lVar == null) - lVar = (OptLocalVariable) vars.get(name); + lVar = (OptLocalVariable) vars.getVariable(name); if (lVar != null) { if (lVar.getJRegister() == -1) if (lVar.isNumber()) @@ -3303,7 +3311,7 @@ if (true) { OptLocalVariable lVar = (OptLocalVariable)(node.getProp(Node.VARIABLE_PROP)); // XXX is this right? If so, clean up. if (hasVarsInRegs && lVar == null) - lVar = (OptLocalVariable) vars.get(child.getString()); + lVar = (OptLocalVariable) vars.getVariable(child.getString()); if (lVar != null) { generateCodeFromNode(child.getNextSibling(), node, -1, -1); if (lVar.getJRegister() == -1) { diff --git a/js/rhino/org/mozilla/javascript/optimizer/Optimizer.java b/js/rhino/org/mozilla/javascript/optimizer/Optimizer.java index 68a726dfd88f..cd0bb9b7f6d8 100644 --- a/js/rhino/org/mozilla/javascript/optimizer/Optimizer.java +++ b/js/rhino/org/mozilla/javascript/optimizer/Optimizer.java @@ -168,7 +168,7 @@ public class Optimizer { } } for (int i = 0; i < theVariables.size(); i++) { - OptLocalVariable lVar = (OptLocalVariable) theVariables.get(i); + OptLocalVariable lVar = (OptLocalVariable) theVariables.getVariable(i); if (!lVar.isParameter()) { int theType = lVar.getTypeUnion(); if (theType == TypeEvent.NumberType) { @@ -1043,7 +1043,7 @@ public class Optimizer { case TokenStream.SETVAR : { String name = n.getFirstChild().getString(); OptLocalVariable theVar = (OptLocalVariable) - theVariables.get(name); + theVariables.getVariable(name); if (theVar != null) n.putProp(Node.VARIABLE_PROP, theVar); } @@ -1051,7 +1051,7 @@ public class Optimizer { case TokenStream.GETVAR : { String name = n.getString(); OptLocalVariable theVar = (OptLocalVariable) - theVariables.get(name); + theVariables.getVariable(name); if (theVar != null) n.putProp(Node.VARIABLE_PROP, theVar); } diff --git a/js/rhino/src/org/mozilla/classfile/ClassFileWriter.java b/js/rhino/src/org/mozilla/classfile/ClassFileWriter.java index 5273ee830644..c7c582f8214c 100644 --- a/js/rhino/src/org/mozilla/classfile/ClassFileWriter.java +++ b/js/rhino/src/org/mozilla/classfile/ClassFileWriter.java @@ -362,7 +362,8 @@ public class ClassFileWriter extends LabelTable { codeAttribute[index++] = (byte)(varCount >> 8); codeAttribute[index++] = (byte)varCount; for (int i = 0; i < varCount; i++) { - LocalVariable lvar = vars.get(i); + LocalVariable lvar = vars.getVariable(i); + // start pc int startPc = lvar.getStartPC(); codeAttribute[index++] = (byte)(startPc >> 8); diff --git a/js/rhino/src/org/mozilla/javascript/Interpreter.java b/js/rhino/src/org/mozilla/javascript/Interpreter.java index 17ac1928fc62..fbaf5ea70ccb 100644 --- a/js/rhino/src/org/mozilla/javascript/Interpreter.java +++ b/js/rhino/src/org/mozilla/javascript/Interpreter.java @@ -174,8 +174,8 @@ public class Interpreter extends LabelTable { boolean needsActivation = theFunction.requiresActivation() || cx.isGeneratingDebug(); generateICodeFromTree(theFunction.getLastChild(), - varTable, needsActivation, - securityDomain); + varTable, needsActivation, + securityDomain); itsData.itsName = theFunction.getFunctionName(); itsData.itsSourceFile = (String) theFunction.getProp( @@ -1204,6 +1204,7 @@ public class Interpreter extends LabelTable { case TokenStream.ONE : case TokenStream.NULL : case TokenStream.THIS : + case TokenStream.THISFN : case TokenStream.FALSE : case TokenStream.TRUE : case TokenStream.UNDEFINED : @@ -1802,6 +1803,9 @@ public class Interpreter extends LabelTable { case TokenStream.THIS : stack[++stackTop] = thisObj; break; + case TokenStream.THISFN : + stack[++stackTop] = fnOrScript; + break; case TokenStream.FALSE : stack[++stackTop] = Boolean.FALSE; break; diff --git a/js/rhino/src/org/mozilla/javascript/InterpreterData.java b/js/rhino/src/org/mozilla/javascript/InterpreterData.java index 58072fa8df65..60b575b5a11e 100644 --- a/js/rhino/src/org/mozilla/javascript/InterpreterData.java +++ b/js/rhino/src/org/mozilla/javascript/InterpreterData.java @@ -80,7 +80,7 @@ class InterpreterData { public boolean removeBreakpoint(int line) { int offset = getOffset(line); - if (offset != -1 && itsICode[offset] == TokenStream.BREAKPOINT) + if (offset != -1 && itsICode[offset] == (byte) TokenStream.BREAKPOINT) { itsICode[offset] = (byte) TokenStream.LINE; return true; diff --git a/js/rhino/src/org/mozilla/javascript/NodeTransformer.java b/js/rhino/src/org/mozilla/javascript/NodeTransformer.java index ec80e53478e4..9758f9c90ba8 100644 --- a/js/rhino/src/org/mozilla/javascript/NodeTransformer.java +++ b/js/rhino/src/org/mozilla/javascript/NodeTransformer.java @@ -449,7 +449,7 @@ public class NodeTransformer { ((FunctionNode) tree).setRequiresActivation(true); } VariableTable vars = getVariableTable(tree); - if (vars.get(name) != null) { + if (vars.getVariable(name) != null) { if (type == TokenStream.SETNAME) { node.setType(TokenStream.SETVAR); bind.setType(TokenStream.STRING); @@ -489,7 +489,7 @@ public class NodeTransformer { ((FunctionNode) tree).setRequiresActivation(true); } VariableTable vars = getVariableTable(tree); - if (vars.get(name) != null) { + if (vars.getVariable(name) != null) { node.setType(TokenStream.GETVAR); } break; @@ -510,7 +510,9 @@ public class NodeTransformer { while ((node = iterator.nextNode()) != null) { int nodeType = node.getType(); if (inFunction && nodeType == TokenStream.FUNCTION && - node != tree) + node != tree && + ((FunctionNode) node.getProp(Node.FUNCTION_PROP)).getFunctionType() == + FunctionNode.FUNCTION_EXPRESSION_STATEMENT) { // In a function with both "var x" and "function x", // disregard the var statement, independent of order. @@ -531,6 +533,26 @@ public class NodeTransformer { vars.addLocal(n.getString()); } } + String name = (String) tree.getDatum(); + if (inFunction && ((FunctionNode) tree).getFunctionType() == + FunctionNode.FUNCTION_EXPRESSION && + name != null && name.length() > 0 && + vars.getVariable(name) == null) + { + // A function expression needs to have its name as a variable + // (if it isn't already allocated as a variable). See + // ECMA Ch. 13. We add code to the beginning of the function + // to initialize a local variable of the function's name + // to the function value. + vars.addLocal(name); + Node block = tree.getLastChild(); + Node setFn = new Node(TokenStream.POP, + new Node(TokenStream.SETVAR, + new Node(TokenStream.STRING, name), + new Node(TokenStream.PRIMARY, + new Integer(TokenStream.THISFN)))); + block.addChildrenToFront(setFn); + } } protected void addParameters(FunctionNode fnNode) { @@ -580,7 +602,9 @@ public class NodeTransformer { if (left.getType() == TokenStream.NAME) { VariableTable vars = getVariableTable(tree); String name = left.getString(); - if (inFunction && vars.get(name) != null && !inWithStatement()) { + if (inFunction && vars.getVariable(name) != null && + !inWithStatement()) + { // call to a var. Transform to Call(GetVar("a"), b, c) left.setType(TokenStream.GETVAR); // fall through to code to add GetParent diff --git a/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java b/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java index a70ef11b0c41..f5af4e6a1dcf 100644 --- a/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java +++ b/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java @@ -854,12 +854,18 @@ public class ScriptRuntime { /* It must be a string. */ int len = str.length(); char c; - if (len > 0 && '0' <= (c = str.charAt(0)) && c <= '9' && + boolean negate = false; + int i = 0; + if (len > 0 && (c = str.charAt(0)) == '-') { + negate = true; + i = 1; + } + if (len > 0 && '0' <= (c = str.charAt(i)) && c <= '9' && len <= MAX_VALUE_LENGTH) { int index = c - '0'; int oldIndex = 0; - int i = 1; + i++; if (index != 0) { while (i < len && '0' <= (c = str.charAt(i)) && c <= '9') { oldIndex = index; @@ -875,7 +881,7 @@ public class ScriptRuntime { (oldIndex == (Integer.MAX_VALUE / 10) && c < (Integer.MAX_VALUE % 10)))) { - return index; + return negate ? -index : index; } } return NO_INDEX; @@ -1159,9 +1165,14 @@ public class ScriptRuntime { } while (next != null); bound.put(id, bound, value); + /* + This code is causing immense performance problems in + scripts that assign to the variables as a way of creating them. + XXX need strict mode String[] args = { id }; String message = getMessage("msg.assn.create", args); Context.reportWarning(message); + */ return value; } return setProp(bound, id, value, scope); @@ -1947,11 +1958,12 @@ public class ScriptRuntime { public static NativeFunction initFunction(NativeFunction fn, Scriptable scope, String fnName, - Context cx) + Context cx, + boolean doSetName) { fn.setPrototype(ScriptableObject.getClassPrototype(scope, "Function")); fn.setParentScope(scope); - if (fnName != null && fnName.length() != 0) + if (doSetName) setName(scope, fn, scope, fnName); return fn; } diff --git a/js/rhino/src/org/mozilla/javascript/TokenStream.java b/js/rhino/src/org/mozilla/javascript/TokenStream.java index a4d2bd1762e1..cf7eee3d89f1 100644 --- a/js/rhino/src/org/mozilla/javascript/TokenStream.java +++ b/js/rhino/src/org/mozilla/javascript/TokenStream.java @@ -168,47 +168,48 @@ public class TokenStream { SETPARENT = 84, SCOPE = 85, GETSCOPEPARENT = 86, - JTHROW = 87, + THISFN = 87, + JTHROW = 88, // End of interpreter bytecodes - SEMI = 88, // semicolon - LB = 89, // left and right brackets - RB = 90, - LC = 91, // left and right curlies (braces) - RC = 92, - LP = 93, // left and right parentheses - RP = 94, - COMMA = 95, // comma operator - ASSIGN = 96, // assignment ops (= += -= etc.) - HOOK = 97, // conditional (?:) - COLON = 98, - OR = 99, // logical or (||) - AND = 100, // logical and (&&) - EQOP = 101, // equality ops (== !=) - RELOP = 102, // relational ops (< <= > >=) - SHOP = 103, // shift ops (<< >> >>>) - UNARYOP = 104, // unary prefix operator - INC = 105, // increment/decrement (++ --) - DEC = 106, - DOT = 107, // member operator (.) - PRIMARY = 108, // true, false, null, this - FUNCTION = 109, // function keyword - EXPORT = 110, // export keyword - IMPORT = 111, // import keyword - IF = 112, // if keyword - ELSE = 113, // else keyword - SWITCH = 114, // switch keyword - CASE = 115, // case keyword - DEFAULT = 116, // default keyword - WHILE = 117, // while keyword - DO = 118, // do keyword - FOR = 119, // for keyword - BREAK = 120, // break keyword - CONTINUE = 121, // continue keyword - VAR = 122, // var keyword - WITH = 123, // with keyword - CATCH = 124, // catch keyword - FINALLY = 125, // finally keyword - RESERVED = 126, // reserved keywords + SEMI = 89, // semicolon + LB = 90, // left and right brackets + RB = 91, + LC = 92, // left and right curlies (braces) + RC = 93, + LP = 94, // left and right parentheses + RP = 95, + COMMA = 96, // comma operator + ASSIGN = 97, // assignment ops (= += -= etc.) + HOOK = 98, // conditional (?:) + COLON = 99, + OR = 100, // logical or (||) + AND = 101, // logical and (&&) + EQOP = 102, // equality ops (== !=) + RELOP = 103, // relational ops (< <= > >=) + SHOP = 104, // shift ops (<< >> >>>) + UNARYOP = 105, // unary prefix operator + INC = 106, // increment/decrement (++ --) + DEC = 107, + DOT = 108, // member operator (.) + PRIMARY = 109, // true, false, null, this + FUNCTION = 110, // function keyword + EXPORT = 111, // export keyword + IMPORT = 112, // import keyword + IF = 113, // if keyword + ELSE = 114, // else keyword + SWITCH = 115, // switch keyword + CASE = 116, // case keyword + DEFAULT = 117, // default keyword + WHILE = 118, // while keyword + DO = 119, // do keyword + FOR = 120, // for keyword + BREAK = 121, // break keyword + CONTINUE = 122, // continue keyword + VAR = 123, // var keyword + WITH = 124, // with keyword + CATCH = 125, // catch keyword + FINALLY = 126, // finally keyword + RESERVED = 127, // reserved keywords /** Added by Mike - these are JSOPs in the jsref, but I * don't have them yet in the java implementation... @@ -217,45 +218,45 @@ public class TokenStream { * Most of these go in the 'op' field when returning * more general token types, eg. 'DIV' as the op of 'ASSIGN'. */ - NOP = 127, // NOP - NOT = 128, // etc. - PRE = 129, // for INC, DEC nodes. - POST = 130, + NOP = 128, // NOP + NOT = 129, // etc. + PRE = 130, // for INC, DEC nodes. + POST = 131, /** * For JSOPs associated with keywords... * eg. op = THIS; token = PRIMARY */ - VOID = 131, + VOID = 132, /* types used for the parse tree - these never get returned * by the scanner. */ - BLOCK = 132, // statement block - ARRAYLIT = 133, // array literal - OBJLIT = 134, // object literal - LABEL = 135, // label - TARGET = 136, - LOOP = 137, - ENUMDONE = 138, - EXPRSTMT = 139, - PARENT = 140, - CONVERT = 141, - JSR = 142, - NEWLOCAL = 143, - USELOCAL = 144, - SCRIPT = 145, // top-level node for entire script + BLOCK = 133, // statement block + ARRAYLIT = 134, // array literal + OBJLIT = 135, // object literal + LABEL = 136, // label + TARGET = 137, + LOOP = 138, + ENUMDONE = 139, + EXPRSTMT = 140, + PARENT = 141, + CONVERT = 142, + JSR = 143, + NEWLOCAL = 144, + USELOCAL = 145, + SCRIPT = 146, // top-level node for entire script /** * For the interpreted mode indicating a line number change in icodes. */ - LINE = 146, - SOURCEFILE = 147, + LINE = 147, + SOURCEFILE = 148, // For debugger - BREAKPOINT = 148; + BREAKPOINT = 149; // end enum @@ -354,6 +355,7 @@ public class TokenStream { "setparent", "scope", "getscopeparent", + "thisfn", "jthrow", "semi", "lb", diff --git a/js/rhino/src/org/mozilla/javascript/debug/DebuggableEngine.java b/js/rhino/src/org/mozilla/javascript/debug/DebuggableEngine.java index b67c82ddfa32..dc066bd412b3 100644 --- a/js/rhino/src/org/mozilla/javascript/debug/DebuggableEngine.java +++ b/js/rhino/src/org/mozilla/javascript/debug/DebuggableEngine.java @@ -49,4 +49,6 @@ public interface DebuggableEngine { public Debugger getDebugger(); public Frame getFrame(int frameNumber); + + //public void haltExecution(); } diff --git a/js/rhino/src/org/mozilla/javascript/debug/DebuggableScript.java b/js/rhino/src/org/mozilla/javascript/debug/DebuggableScript.java index 835ccb3f26aa..db86b6973185 100644 --- a/js/rhino/src/org/mozilla/javascript/debug/DebuggableScript.java +++ b/js/rhino/src/org/mozilla/javascript/debug/DebuggableScript.java @@ -46,6 +46,8 @@ import java.util.Enumeration; * code (either functions or top-level scripts). */ public interface DebuggableScript { + + //public boolean isFunction(); // XXX /** * Get the Scriptable object (Function or Script) that is diff --git a/js/rhino/src/org/mozilla/javascript/optimizer/Block.java b/js/rhino/src/org/mozilla/javascript/optimizer/Block.java index 591306786ba0..a37866d87cd9 100644 --- a/js/rhino/src/org/mozilla/javascript/optimizer/Block.java +++ b/js/rhino/src/org/mozilla/javascript/optimizer/Block.java @@ -217,7 +217,7 @@ public class Block { } for (int i = 0; i < liveSet.length; i++) { if (liveSet[i]) - ((OptLocalVariable)theVariables.get(i)).markLiveAcrossCall(); + ((OptLocalVariable)theVariables.getVariable(i)).markLiveAcrossCall(); } } break; @@ -246,7 +246,7 @@ public class Block { { for (int i = 0; i < theVariables.size(); i++) if (itsLiveOnEntrySet.test(i)) - ((OptLocalVariable)theVariables.get(i)).assignType(TypeEvent.AnyType); + ((OptLocalVariable)theVariables.getVariable(i)).assignType(TypeEvent.AnyType); } @@ -650,13 +650,13 @@ public class Block { { for (int i = 0; i < theVariables.size(); i++) { if (itsUseBeforeDefSet.test(i)) - pw.println(theVariables.get(i).getName() + " is used before def'd"); + pw.println(theVariables.getVariable(i).getName() + " is used before def'd"); if (itsNotDefSet.test(i)) - pw.println(theVariables.get(i).getName() + " is not def'd"); + pw.println(theVariables.getVariable(i).getName() + " is not def'd"); if (itsLiveOnEntrySet.test(i)) - pw.println(theVariables.get(i).getName() + " is live on entry"); + pw.println(theVariables.getVariable(i).getName() + " is live on entry"); if (itsLiveOnExitSet.test(i)) - pw.println(theVariables.get(i).getName() + " is live on exit"); + pw.println(theVariables.getVariable(i).getName() + " is live on exit"); } } diff --git a/js/rhino/src/org/mozilla/javascript/optimizer/Codegen.java b/js/rhino/src/org/mozilla/javascript/optimizer/Codegen.java index aab4d5f6a413..9c3e10152d5b 100644 --- a/js/rhino/src/org/mozilla/javascript/optimizer/Codegen.java +++ b/js/rhino/src/org/mozilla/javascript/optimizer/Codegen.java @@ -457,7 +457,7 @@ public class Codegen extends Interpreter { // make sure that all parameters are objects itsForcedObjectParameters = true; for (int i = 0; i < vars.getParameterCount(); i++) { - OptLocalVariable lVar = (OptLocalVariable) vars.get(i); + OptLocalVariable lVar = (OptLocalVariable) vars.getVariable(i); aload(lVar.getJRegister()); classFile.add(ByteCode.GETSTATIC, "java/lang/Void", @@ -1347,12 +1347,18 @@ public class Codegen extends Interpreter { addByteCode(ByteCode.ACONST_NULL); } // load 'cx' - aload(contextLocal); + aload(contextLocal); + // load boolean indicating whether fn name should be set in scope + boolean setFnName = str != null && str.length() > 0 && + ((FunctionNode) def).getFunctionType() != + FunctionNode.FUNCTION_EXPRESSION; + addByteCode(setFnName ? ByteCode.ICONST_1 : ByteCode.ICONST_0); + addScriptRuntimeInvoke("initFunction", "(Lorg/mozilla/javascript/NativeFunction;" + "Lorg/mozilla/javascript/Scriptable;" + "Ljava/lang/String;" + - "Lorg/mozilla/javascript/Context;)", + "Lorg/mozilla/javascript/Context;Z)", "Lorg/mozilla/javascript/NativeFunction;"); def.putProp(Node.FUNCTION_PROP, new Short(i)); addByteCode(ByteCode.AASTORE); // store NativeFunction @@ -1447,7 +1453,7 @@ public class Codegen extends Interpreter { // before the next call and are used in the function short firstUndefVar = -1; for (int i = 0; i < vars.size(); i++) { - OptLocalVariable lVar = (OptLocalVariable) vars.get(i); + OptLocalVariable lVar = (OptLocalVariable) vars.getVariable(i); if (lVar.isNumber()) { lVar.assignJRegister(getNewWordPairLocal()); push(0.0); @@ -1547,7 +1553,7 @@ public class Codegen extends Interpreter { if (cx.isGeneratingDebug()) { debugVars = new OptVariableTable(); debugVars.addLocal(debugVariableName); - OptLocalVariable lv = (OptLocalVariable) debugVars.get(debugVariableName); + OptLocalVariable lv = (OptLocalVariable) debugVars.getVariable(debugVariableName); lv.assignJRegister(variableObjectLocal); lv.setStartPC(classFile.getCurrentCodeOffset()); } @@ -1876,7 +1882,6 @@ public class Codegen extends Interpreter { */ { -if (true) { Node callBase = callNode.getFirstChild(); if (callBase.getType() == TokenStream.GETPROP) { Node callBaseChild = callBase.getFirstChild(); @@ -1903,7 +1908,6 @@ if (true) { } } } -} return null; } @@ -2425,7 +2429,7 @@ if (true) { } String name = node.getString(); if (hasVarsInRegs) { - OptLocalVariable lVar = (OptLocalVariable) vars.get(name); + OptLocalVariable lVar = (OptLocalVariable) vars.getVariable(name); if (lVar != null) { if (lVar.isNumber()) { push("number"); @@ -2463,7 +2467,7 @@ if (true) { String routine = (isInc) ? "postIncrement" : "postDecrement"; if (hasVarsInRegs && child.getType() == TokenStream.GETVAR) { if (lVar == null) - lVar = (OptLocalVariable) vars.get(child.getString()); + lVar = (OptLocalVariable) vars.getVariable(child.getString()); if (lVar.getJRegister() == -1) lVar.assignJRegister(getNewWordLocal()); aload(lVar.getJRegister()); @@ -3173,6 +3177,10 @@ if (true) { aload(thisObjLocal); break; + case TokenStream.THISFN: + classFile.add(ByteCode.ALOAD_0); + break; + case TokenStream.NULL: addByteCode(ByteCode.ACONST_NULL); break; @@ -3232,7 +3240,7 @@ if (true) { { // TODO: Clean up use of lVar here and in set. if (hasVarsInRegs && lVar == null) - lVar = (OptLocalVariable) vars.get(name); + lVar = (OptLocalVariable) vars.getVariable(name); if (lVar != null) { if (lVar.getJRegister() == -1) if (lVar.isNumber()) @@ -3303,7 +3311,7 @@ if (true) { OptLocalVariable lVar = (OptLocalVariable)(node.getProp(Node.VARIABLE_PROP)); // XXX is this right? If so, clean up. if (hasVarsInRegs && lVar == null) - lVar = (OptLocalVariable) vars.get(child.getString()); + lVar = (OptLocalVariable) vars.getVariable(child.getString()); if (lVar != null) { generateCodeFromNode(child.getNextSibling(), node, -1, -1); if (lVar.getJRegister() == -1) { diff --git a/js/rhino/src/org/mozilla/javascript/optimizer/Optimizer.java b/js/rhino/src/org/mozilla/javascript/optimizer/Optimizer.java index 68a726dfd88f..cd0bb9b7f6d8 100644 --- a/js/rhino/src/org/mozilla/javascript/optimizer/Optimizer.java +++ b/js/rhino/src/org/mozilla/javascript/optimizer/Optimizer.java @@ -168,7 +168,7 @@ public class Optimizer { } } for (int i = 0; i < theVariables.size(); i++) { - OptLocalVariable lVar = (OptLocalVariable) theVariables.get(i); + OptLocalVariable lVar = (OptLocalVariable) theVariables.getVariable(i); if (!lVar.isParameter()) { int theType = lVar.getTypeUnion(); if (theType == TypeEvent.NumberType) { @@ -1043,7 +1043,7 @@ public class Optimizer { case TokenStream.SETVAR : { String name = n.getFirstChild().getString(); OptLocalVariable theVar = (OptLocalVariable) - theVariables.get(name); + theVariables.getVariable(name); if (theVar != null) n.putProp(Node.VARIABLE_PROP, theVar); } @@ -1051,7 +1051,7 @@ public class Optimizer { case TokenStream.GETVAR : { String name = n.getString(); OptLocalVariable theVar = (OptLocalVariable) - theVariables.get(name); + theVariables.getVariable(name); if (theVar != null) n.putProp(Node.VARIABLE_PROP, theVar); }