зеркало из https://github.com/mozilla/pjs.git
Fix Bug 370400 - Rhino should support const keyword
Patch provided by Bob Jervis (bjervis@google.com)
This commit is contained in:
Родитель
a4385f9f64
Коммит
3f6be5befa
|
@ -0,0 +1,116 @@
|
|||
/* -*- 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):
|
||||
* Bob Jervis
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
// API class
|
||||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: bjervis
|
||||
* Date: Feb 14, 2007
|
||||
* Time: 11:04:59 AM
|
||||
* To change this template use File | Settings | File Templates.
|
||||
*/
|
||||
public interface ConstProperties {
|
||||
/**
|
||||
* Sets a named const property in this object.
|
||||
* <p>
|
||||
* The property is specified by a string name
|
||||
* as defined for <code>Scriptable.get</code>.
|
||||
* <p>
|
||||
* The possible values that may be passed in are as defined for
|
||||
* <code>Scriptable.get</code>. A class that implements this method may choose
|
||||
* to ignore calls to set certain properties, in which case those
|
||||
* properties are effectively read-only.<p>
|
||||
* For properties defined in a prototype chain,
|
||||
* use <code>putProperty</code> in ScriptableObject. <p>
|
||||
* Note that if a property <i>a</i> is defined in the prototype <i>p</i>
|
||||
* of an object <i>o</i>, then evaluating <code>o.a = 23</code> will cause
|
||||
* <code>set</code> to be called on the prototype <i>p</i> with
|
||||
* <i>o</i> as the <i>start</i> parameter.
|
||||
* To preserve JavaScript semantics, it is the Scriptable
|
||||
* object's responsibility to modify <i>o</i>. <p>
|
||||
* This design allows properties to be defined in prototypes and implemented
|
||||
* in terms of getters and setters of Java values without consuming slots
|
||||
* in each instance.<p>
|
||||
* <p>
|
||||
* The values that may be set are limited to the following:
|
||||
* <UL>
|
||||
* <LI>java.lang.Boolean objects</LI>
|
||||
* <LI>java.lang.String objects</LI>
|
||||
* <LI>java.lang.Number objects</LI>
|
||||
* <LI>org.mozilla.javascript.Scriptable objects</LI>
|
||||
* <LI>null</LI>
|
||||
* <LI>The value returned by Context.getUndefinedValue()</LI>
|
||||
* </UL><p>
|
||||
* Arbitrary Java objects may be wrapped in a Scriptable by first calling
|
||||
* <code>Context.toObject</code>. This allows the property of a JavaScript
|
||||
* object to contain an arbitrary Java object as a value.<p>
|
||||
* Note that <code>has</code> will be called by the runtime first before
|
||||
* <code>set</code> is called to determine in which object the
|
||||
* property is defined.
|
||||
* Note that this method is not expected to traverse the prototype chain,
|
||||
* which is different from the ECMA [[Put]] operation.
|
||||
* @param name the name of the property
|
||||
* @param start the object whose property is being set
|
||||
* @param value value to set the property to
|
||||
* @see org.mozilla.javascript.Scriptable#has
|
||||
* @see org.mozilla.javascript.Scriptable#get
|
||||
* @see org.mozilla.javascript.ScriptableObject#putProperty
|
||||
* @see org.mozilla.javascript.Context#toObject
|
||||
*/
|
||||
public void putConst(String name, Scriptable start, Object value);
|
||||
|
||||
/**
|
||||
* Reserves a definition spot for a const. This will set up a definition
|
||||
* of the const property, but set its value to undefined. The semantics of
|
||||
* the start parameter is the same as for putConst.
|
||||
* @param name The name of the property.
|
||||
* @param start The object whose property is being reserved.
|
||||
*/
|
||||
public void defineConst(String name, Scriptable start);
|
||||
|
||||
/**
|
||||
* Returns true if the named property is defined as a const on this object.
|
||||
* @param name
|
||||
* @return true if the named property is defined as a const, false
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isConst(String name);
|
||||
}
|
|
@ -744,6 +744,10 @@ public class Decompiler
|
|||
result.append("void ");
|
||||
break;
|
||||
|
||||
case Token.CONST:
|
||||
result.append("const ");
|
||||
break;
|
||||
|
||||
case Token.NOT:
|
||||
result.append('!');
|
||||
break;
|
||||
|
@ -806,7 +810,8 @@ public class Decompiler
|
|||
|
||||
default:
|
||||
// If we don't know how to decompile it, raise an exception.
|
||||
throw new RuntimeException();
|
||||
throw new RuntimeException("Token: " +
|
||||
Token.name(source.charAt(i)));
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
* Norris Boyd
|
||||
* Igor Bukanov
|
||||
* Ethan Hugg
|
||||
* Bob Jervis
|
||||
* Terry Lucas
|
||||
* Milen Nankov
|
||||
*
|
||||
|
@ -177,9 +178,9 @@ final class IRFactory
|
|||
switchBlock.addChildToBack(switchBreakTarget);
|
||||
}
|
||||
|
||||
Node createVariables(int lineno)
|
||||
Node createVariables(int token, int lineno)
|
||||
{
|
||||
return new Node(Token.VAR, lineno);
|
||||
return new Node(token, lineno);
|
||||
}
|
||||
|
||||
Node createExprStatement(Node expr, int lineno)
|
||||
|
@ -379,7 +380,8 @@ final class IRFactory
|
|||
// 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.
|
||||
fnNode.addVar(name);
|
||||
if (!fnNode.addVar(name))
|
||||
parser.addError("msg.const.redecl", name);
|
||||
Node setFn = new Node(Token.EXPR_VOID,
|
||||
new Node(Token.SETNAME,
|
||||
Node.newString(Token.BINDNAME, name),
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Igor Bukanov
|
||||
* Bob Jervis
|
||||
* Roger Lawrence
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
|
@ -147,8 +148,8 @@ final class InterpretedFunction extends NativeFunction implements Script
|
|||
* Calls the function.
|
||||
* @param cx the current context
|
||||
* @param scope the scope used for the call
|
||||
* @param the value of "this"
|
||||
* @param function arguments. Must not be null. You can use
|
||||
* @param thisObj the value of "this"
|
||||
* @param args function arguments. Must not be null. You can use
|
||||
* {@link ScriptRuntime#emptyArgs} to pass empty arguments.
|
||||
* @return the result of the function call.
|
||||
*/
|
||||
|
@ -206,5 +207,9 @@ final class InterpretedFunction extends NativeFunction implements Script
|
|||
return idata.argNames[index];
|
||||
}
|
||||
|
||||
protected boolean getParamOrVarConst(int index)
|
||||
{
|
||||
return idata.argIsConst[index];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -167,8 +167,13 @@ public class Interpreter
|
|||
Icode_LITERAL_GETTER = -57,
|
||||
Icode_LITERAL_SETTER = -58,
|
||||
|
||||
// const
|
||||
Icode_SETCONST = -59,
|
||||
Icode_SETCONSTVAR = -60,
|
||||
Icode_SETCONSTVAR1 = -61,
|
||||
|
||||
// Last icode
|
||||
MIN_ICODE = -58;
|
||||
MIN_ICODE = -61;
|
||||
|
||||
// data for parsing
|
||||
|
||||
|
@ -231,6 +236,7 @@ public class Interpreter
|
|||
// sDbl[i]: if stack[i] is UniqueTag.DOUBLE_MARK, sDbl[i] holds the number value
|
||||
|
||||
Object[] stack;
|
||||
int[] stackAttributes;
|
||||
double[] sDbl;
|
||||
CallFrame varSource; // defaults to this unless continuation frame
|
||||
int localShift;
|
||||
|
@ -269,6 +275,7 @@ public class Interpreter
|
|||
// from this frame to share variables.
|
||||
|
||||
copy.stack = (Object[])stack.clone();
|
||||
copy.stackAttributes = (int[])stackAttributes.clone();
|
||||
copy.sDbl = (double[])sDbl.clone();
|
||||
|
||||
copy.frozen = false;
|
||||
|
@ -419,6 +426,9 @@ public class Interpreter
|
|||
case Icode_LOCAL_CLEAR: return "LOCAL_CLEAR";
|
||||
case Icode_LITERAL_GETTER: return "LITERAL_GETTER";
|
||||
case Icode_LITERAL_SETTER: return "LITERAL_SETTER";
|
||||
case Icode_SETCONST: return "SETCONST";
|
||||
case Icode_SETCONSTVAR: return "SETCONSTVAR";
|
||||
case Icode_SETCONSTVAR1: return "SETCONSTVAR1";
|
||||
}
|
||||
|
||||
// icode without name
|
||||
|
@ -568,6 +578,7 @@ public class Interpreter
|
|||
+ itsData.itsMaxStack;
|
||||
|
||||
itsData.argNames = scriptOrFn.getParamAndVarNames();
|
||||
itsData.argIsConst = scriptOrFn.getParamAndVarConst();
|
||||
itsData.argCount = scriptOrFn.getParamCount();
|
||||
|
||||
itsData.encodedSourceStart = scriptOrFn.getEncodedSourceStart();
|
||||
|
@ -657,9 +668,9 @@ public class Interpreter
|
|||
}
|
||||
}
|
||||
// For function statements or function expression statements
|
||||
// in scripts, we need to ensure that the result of the script
|
||||
// in scripts, we need to ensure that the result of the script
|
||||
// is the function if it is the last statement in the script.
|
||||
// For example, eval("function () {}") should return a
|
||||
// For example, eval("function () {}") should return a
|
||||
// function, not undefined.
|
||||
if (!itsInFunctionFlag) {
|
||||
addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
|
||||
|
@ -1137,6 +1148,17 @@ public class Interpreter
|
|||
}
|
||||
break;
|
||||
|
||||
case Token.SETCONST:
|
||||
{
|
||||
String name = child.getString();
|
||||
visitExpression(child, 0);
|
||||
child = child.getNext();
|
||||
visitExpression(child, 0);
|
||||
addStringOp(Icode_SETCONST, name);
|
||||
stackChange(-1);
|
||||
}
|
||||
break;
|
||||
|
||||
case Token.TYPEOFNAME:
|
||||
{
|
||||
String name = node.getString();
|
||||
|
@ -1218,6 +1240,17 @@ public class Interpreter
|
|||
}
|
||||
break;
|
||||
|
||||
case Token.SETCONSTVAR:
|
||||
{
|
||||
if (itsData.itsNeedsActivation) Kit.codeBug();
|
||||
String name = child.getString();
|
||||
child = child.getNext();
|
||||
visitExpression(child, 0);
|
||||
int index = scriptOrFn.getParamOrVarIndex(name);
|
||||
addVarOp(Token.SETCONSTVAR, index);
|
||||
}
|
||||
break;
|
||||
|
||||
case Token.NULL:
|
||||
case Token.THIS:
|
||||
case Token.THISFN:
|
||||
|
@ -1635,6 +1668,14 @@ public class Interpreter
|
|||
private void addVarOp(int op, int varIndex)
|
||||
{
|
||||
switch (op) {
|
||||
case Token.SETCONSTVAR:
|
||||
if (varIndex < 128) {
|
||||
addIcode(Icode_SETCONSTVAR1);
|
||||
addUint8(varIndex);
|
||||
return;
|
||||
}
|
||||
addIndexOp(Icode_SETCONSTVAR, varIndex);
|
||||
return;
|
||||
case Token.GETVAR:
|
||||
case Token.SETVAR:
|
||||
if (varIndex < 128) {
|
||||
|
@ -1985,6 +2026,7 @@ public class Interpreter
|
|||
}
|
||||
case Icode_GETVAR1:
|
||||
case Icode_SETVAR1:
|
||||
case Icode_SETCONSTVAR1:
|
||||
indexReg = iCode[pc];
|
||||
out.println(tname+" "+indexReg);
|
||||
++pc;
|
||||
|
@ -2084,6 +2126,7 @@ public class Interpreter
|
|||
|
||||
case Icode_GETVAR1:
|
||||
case Icode_SETVAR1:
|
||||
case Icode_SETCONSTVAR1:
|
||||
// byte var index
|
||||
return 1 + 1;
|
||||
|
||||
|
@ -2459,6 +2502,7 @@ public class Interpreter
|
|||
double[] sDbl = frame.sDbl;
|
||||
Object[] vars = frame.varSource.stack;
|
||||
double[] varDbls = frame.varSource.sDbl;
|
||||
int[] varAttributes = frame.varSource.stackAttributes;
|
||||
byte[] iCode = frame.idata.itsICode;
|
||||
String[] strings = frame.idata.itsStringTable;
|
||||
|
||||
|
@ -2830,6 +2874,14 @@ switch (op) {
|
|||
frame.scope, stringReg);
|
||||
continue Loop;
|
||||
}
|
||||
case Icode_SETCONST: {
|
||||
Object rhs = stack[stackTop];
|
||||
if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
|
||||
--stackTop;
|
||||
Scriptable lhs = (Scriptable)stack[stackTop];
|
||||
stack[stackTop] = ScriptRuntime.setConst(lhs, rhs, cx, stringReg);
|
||||
continue Loop;
|
||||
}
|
||||
case Token.DELPROP : {
|
||||
Object rhs = stack[stackTop];
|
||||
if (rhs == DBL_MRK) rhs = ScriptRuntime.wrapNumber(sDbl[stackTop]);
|
||||
|
@ -3198,13 +3250,42 @@ switch (op) {
|
|||
cx, iCode[frame.pc]);
|
||||
++frame.pc;
|
||||
continue Loop;
|
||||
case Icode_SETCONSTVAR1:
|
||||
indexReg = iCode[frame.pc++];
|
||||
// fallthrough
|
||||
case Token.SETCONSTVAR :
|
||||
if (!frame.useActivation) {
|
||||
if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) {
|
||||
throw Context.reportRuntimeError1("msg.var.redecl",
|
||||
frame.idata.argNames[indexReg]);
|
||||
}
|
||||
if ((varAttributes[indexReg] & ScriptableObject.UNINITIALIZED_CONST)
|
||||
!= 0)
|
||||
{
|
||||
vars[indexReg] = stack[stackTop];
|
||||
varAttributes[indexReg] &= ~ScriptableObject.UNINITIALIZED_CONST;
|
||||
varDbls[indexReg] = sDbl[stackTop];
|
||||
}
|
||||
} else {
|
||||
Object val = stack[stackTop];
|
||||
if (val == DBL_MRK) val = ScriptRuntime.wrapNumber(sDbl[stackTop]);
|
||||
stringReg = frame.idata.argNames[indexReg];
|
||||
if (frame.scope instanceof ConstProperties) {
|
||||
ConstProperties cp = (ConstProperties)frame.scope;
|
||||
cp.putConst(stringReg, frame.scope, val);
|
||||
} else
|
||||
throw Kit.codeBug();
|
||||
}
|
||||
continue Loop;
|
||||
case Icode_SETVAR1:
|
||||
indexReg = iCode[frame.pc++];
|
||||
// fallthrough
|
||||
case Token.SETVAR :
|
||||
if (!frame.useActivation) {
|
||||
vars[indexReg] = stack[stackTop];
|
||||
varDbls[indexReg] = sDbl[stackTop];
|
||||
if ((varAttributes[indexReg] & ScriptableObject.READONLY) == 0) {
|
||||
vars[indexReg] = stack[stackTop];
|
||||
varDbls[indexReg] = sDbl[stackTop];
|
||||
}
|
||||
} else {
|
||||
Object val = stack[stackTop];
|
||||
if (val == DBL_MRK) val = ScriptRuntime.wrapNumber(sDbl[stackTop]);
|
||||
|
@ -3822,19 +3903,27 @@ switch (op) {
|
|||
Kit.codeBug();
|
||||
|
||||
Object[] stack;
|
||||
int[] stackAttributes;
|
||||
double[] sDbl;
|
||||
boolean stackReuse;
|
||||
if (frame.stack != null && maxFrameArray <= frame.stack.length) {
|
||||
// Reuse stacks from old frame
|
||||
stackReuse = true;
|
||||
stack = frame.stack;
|
||||
stackAttributes = frame.stackAttributes;
|
||||
sDbl = frame.sDbl;
|
||||
} else {
|
||||
stackReuse = false;
|
||||
stack = new Object[maxFrameArray];
|
||||
stackAttributes = new int[maxFrameArray];
|
||||
sDbl = new double[maxFrameArray];
|
||||
}
|
||||
|
||||
int varCount = idata.getParamAndVarCount();
|
||||
for (int i = 0; i < varCount; i++) {
|
||||
if (idata.getParamOrVarConst(i))
|
||||
stackAttributes[i] = ScriptableObject.CONST;
|
||||
}
|
||||
int definedArgs = idata.argCount;
|
||||
if (definedArgs > argCount) { definedArgs = argCount; }
|
||||
|
||||
|
@ -3853,6 +3942,7 @@ switch (op) {
|
|||
frame.idata = idata;
|
||||
|
||||
frame.stack = stack;
|
||||
frame.stackAttributes = stackAttributes;
|
||||
frame.sDbl = sDbl;
|
||||
frame.varSource = frame;
|
||||
frame.localShift = idata.itsMaxVars;
|
||||
|
@ -4015,6 +4105,7 @@ switch (op) {
|
|||
for (int i = x.savedStackTop + 1; i != x.stack.length; ++i) {
|
||||
// Allow to GC unused stack space
|
||||
x.stack[i] = null;
|
||||
x.stackAttributes[i] = ScriptableObject.EMPTY;
|
||||
}
|
||||
if (x.savedCallOp == Token.CALL) {
|
||||
// the call will always overwrite the stack top with the result
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* Bob Jervis
|
||||
* Roger Lawrence
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
|
@ -98,6 +99,7 @@ final class InterpreterData implements Serializable, DebuggableScript
|
|||
|
||||
// see comments in NativeFuncion for definition of argNames and argCount
|
||||
String[] argNames;
|
||||
boolean[] argIsConst;
|
||||
int argCount;
|
||||
|
||||
int itsMaxCalleeArgs;
|
||||
|
@ -152,6 +154,11 @@ final class InterpreterData implements Serializable, DebuggableScript
|
|||
return argNames[index];
|
||||
}
|
||||
|
||||
public boolean getParamOrVarConst(int index)
|
||||
{
|
||||
return argIsConst[index];
|
||||
}
|
||||
|
||||
public String getSourceName()
|
||||
{
|
||||
return itsSourceFile;
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* Bob Jervis
|
||||
*
|
||||
* 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
|
||||
|
@ -73,7 +74,7 @@ public final class NativeCall extends IdScriptableObject
|
|||
int paramAndVarCount = function.getParamAndVarCount();
|
||||
int paramCount = function.getParamCount();
|
||||
if (paramAndVarCount != 0) {
|
||||
for (int i = 0; i != paramCount; ++i) {
|
||||
for (int i = 0; i < paramCount; ++i) {
|
||||
String name = function.getParamOrVarName(i);
|
||||
Object val = i < args.length ? args[i]
|
||||
: Undefined.instance;
|
||||
|
@ -88,10 +89,13 @@ public final class NativeCall extends IdScriptableObject
|
|||
}
|
||||
|
||||
if (paramAndVarCount != 0) {
|
||||
for (int i = paramCount; i != paramAndVarCount; ++i) {
|
||||
for (int i = paramCount; i < paramAndVarCount; ++i) {
|
||||
String name = function.getParamOrVarName(i);
|
||||
if (!super.has(name, this)) {
|
||||
defineProperty(name, Undefined.instance, PERMANENT);
|
||||
if (function.getParamOrVarConst(i))
|
||||
defineProperty(name, Undefined.instance, CONST);
|
||||
else
|
||||
defineProperty(name, Undefined.instance, PERMANENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* Igor Bukanov
|
||||
* Bob Jervis
|
||||
* Roger Lawrence
|
||||
* Mike McCabe
|
||||
*
|
||||
|
@ -134,5 +135,13 @@ public abstract class NativeFunction extends BaseFunction
|
|||
* corresponding parameter. Otherwise returm the name of variable.
|
||||
*/
|
||||
protected abstract String getParamOrVarName(int index);
|
||||
|
||||
/**
|
||||
* Get parameter or variable const-ness.
|
||||
* If <tt>index < {@link #getParamCount()}</tt>, then return the const-ness
|
||||
* of the corresponding parameter. Otherwise returm whether the variable is
|
||||
* const.
|
||||
*/
|
||||
protected abstract boolean getParamOrVarConst(int index);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* Igor Bukanov
|
||||
* Bob Jervis
|
||||
* Roger Lawrence
|
||||
* Mike McCabe
|
||||
*
|
||||
|
@ -236,6 +237,7 @@ public class NodeTransformer
|
|||
visitNew(node, tree);
|
||||
break;
|
||||
|
||||
case Token.CONST:
|
||||
case Token.VAR:
|
||||
{
|
||||
Node result = new Node(Token.BLOCK);
|
||||
|
@ -250,7 +252,10 @@ public class NodeTransformer
|
|||
Node init = n.getFirstChild();
|
||||
n.removeChild(init);
|
||||
n.setType(Token.BINDNAME);
|
||||
n = new Node(Token.SETNAME, n, init);
|
||||
n = new Node(type == Token.VAR ?
|
||||
Token.SETNAME :
|
||||
Token.SETCONST,
|
||||
n, init);
|
||||
Node pop = new Node(Token.EXPR_VOID, n, node.getLineno());
|
||||
result.addChildToBack(pop);
|
||||
}
|
||||
|
@ -260,6 +265,7 @@ public class NodeTransformer
|
|||
|
||||
case Token.NAME:
|
||||
case Token.SETNAME:
|
||||
case Token.SETCONST:
|
||||
case Token.DELPROP:
|
||||
{
|
||||
// Turn name to var for faster access if possible
|
||||
|
@ -287,6 +293,9 @@ public class NodeTransformer
|
|||
} else if (type == Token.SETNAME) {
|
||||
node.setType(Token.SETVAR);
|
||||
nameSource.setType(Token.STRING);
|
||||
} else if (type == Token.SETCONST) {
|
||||
node.setType(Token.SETCONSTVAR);
|
||||
nameSource.setType(Token.STRING);
|
||||
} else if (type == Token.DELPROP) {
|
||||
// Local variables are by definition permanent
|
||||
Node n = new Node(Token.FALSE);
|
||||
|
|
|
@ -128,6 +128,14 @@ public class Parser
|
|||
ts.getLine(), ts.getOffset());
|
||||
}
|
||||
|
||||
void addError(String messageId, String messageArg)
|
||||
{
|
||||
++syntaxErrorCount;
|
||||
String message = ScriptRuntime.getMessage1(messageId, messageArg);
|
||||
errorReporter.error(message, sourceURI, ts.getLineno(),
|
||||
ts.getLine(), ts.getOffset());
|
||||
}
|
||||
|
||||
RuntimeException reportError(String messageId)
|
||||
{
|
||||
addError(messageId);
|
||||
|
@ -815,7 +823,7 @@ public class Parser
|
|||
if (tt == Token.VAR) {
|
||||
// set init to a var list or initial
|
||||
consumeToken(); // consume the 'var' token
|
||||
init = variables(true);
|
||||
init = variables(Token.FOR);
|
||||
}
|
||||
else {
|
||||
init = expr(true);
|
||||
|
@ -1017,9 +1025,10 @@ public class Parser
|
|||
return pn;
|
||||
}
|
||||
|
||||
case Token.CONST:
|
||||
case Token.VAR: {
|
||||
consumeToken();
|
||||
pn = variables(false);
|
||||
pn = variables(tt);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1181,13 +1190,28 @@ public class Parser
|
|||
return pn;
|
||||
}
|
||||
|
||||
private Node variables(boolean inForInit)
|
||||
/**
|
||||
* Parse a 'var' or 'const' statement, or a 'var' init list in a for
|
||||
* statement.
|
||||
* @param context A token value: either VAR, CONST or FOR depending on
|
||||
* context.
|
||||
* @return The parsed statement
|
||||
* @throws IOException
|
||||
* @throws ParserException
|
||||
*/
|
||||
private Node variables(int context)
|
||||
throws IOException, ParserException
|
||||
{
|
||||
Node pn = nf.createVariables(ts.getLineno());
|
||||
Node pn;
|
||||
boolean first = true;
|
||||
|
||||
decompiler.addToken(Token.VAR);
|
||||
if (context == Token.CONST){
|
||||
pn = nf.createVariables(Token.CONST, ts.getLineno());
|
||||
decompiler.addToken(Token.CONST);
|
||||
} else {
|
||||
pn = nf.createVariables(Token.VAR, ts.getLineno());
|
||||
decompiler.addToken(Token.VAR);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
Node name;
|
||||
|
@ -1200,7 +1224,19 @@ public class Parser
|
|||
first = false;
|
||||
|
||||
decompiler.addName(s);
|
||||
currentScriptOrFn.addVar(s);
|
||||
if (context == Token.CONST) {
|
||||
if (!currentScriptOrFn.addConst(s)) {
|
||||
// We know it's already defined, since addVar passes if
|
||||
// it's a var.
|
||||
if (currentScriptOrFn.addVar(s))
|
||||
addError("msg.var.redecl", s);
|
||||
else
|
||||
addError("msg.const.redecl", s);
|
||||
}
|
||||
} else {
|
||||
if (!currentScriptOrFn.addVar(s))
|
||||
addError("msg.const.redecl", s);
|
||||
}
|
||||
name = nf.createName(s);
|
||||
|
||||
// omitted check for argument hiding
|
||||
|
@ -1208,7 +1244,7 @@ public class Parser
|
|||
if (matchToken(Token.ASSIGN)) {
|
||||
decompiler.addToken(Token.ASSIGN);
|
||||
|
||||
init = assignExpr(inForInit);
|
||||
init = assignExpr(context == Token.FOR);
|
||||
nf.addChildToBack(name, init);
|
||||
}
|
||||
nf.addChildToBack(pn, name);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Igor Bukanov
|
||||
* Bob Jervis
|
||||
*
|
||||
* 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
|
||||
|
@ -142,24 +143,53 @@ public class ScriptOrFnNode extends Node {
|
|||
return array;
|
||||
}
|
||||
|
||||
public final boolean[] getParamAndVarConst() {
|
||||
int N = itsVariables.size();
|
||||
boolean[] array = new boolean[N];
|
||||
for (int i = 0; i < N; i++)
|
||||
if (itsConst.get(i) != null)
|
||||
array[i] = true;
|
||||
return array;
|
||||
}
|
||||
|
||||
public final void addParam(String name) {
|
||||
// Check addparam is not called after addLocal
|
||||
if (varStart != itsVariables.size()) Kit.codeBug();
|
||||
// Allow non-unique parameter names: use the last occurrence
|
||||
int index = varStart++;
|
||||
itsVariables.add(name);
|
||||
itsConst.add(null);
|
||||
itsVariableNames.put(name, index);
|
||||
}
|
||||
|
||||
public final void addVar(String name) {
|
||||
public final boolean addVar(String name) {
|
||||
int vIndex = itsVariableNames.get(name, -1);
|
||||
if (vIndex != -1) {
|
||||
// There's already a variable or parameter with this name.
|
||||
return;
|
||||
Object v = itsConst.get(vIndex);
|
||||
if (v == null)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
int index = itsVariables.size();
|
||||
itsVariables.add(name);
|
||||
itsConst.add(null);
|
||||
itsVariableNames.put(name, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
public final boolean addConst(String name) {
|
||||
int vIndex = itsVariableNames.get(name, -1);
|
||||
if (vIndex != -1) {
|
||||
// There's already a variable or parameter with this name.
|
||||
return false;
|
||||
}
|
||||
int index = itsVariables.size();
|
||||
itsVariables.add(name);
|
||||
itsConst.add(name);
|
||||
itsVariableNames.put(name, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
public final void removeParamOrVar(String name) {
|
||||
|
@ -202,6 +232,7 @@ public class ScriptOrFnNode extends Node {
|
|||
|
||||
// a list of the formal parameters and local variables
|
||||
private ObjArray itsVariables = new ObjArray();
|
||||
private ObjArray itsConst = new ObjArray();
|
||||
|
||||
// mapping from name to index in list
|
||||
private ObjToIntMap itsVariableNames = new ObjToIntMap(11);
|
||||
|
|
|
@ -1789,6 +1789,18 @@ public class ScriptRuntime {
|
|||
return value;
|
||||
}
|
||||
|
||||
public static Object setConst(Scriptable bound, Object value,
|
||||
Context cx, String id)
|
||||
{
|
||||
if (bound instanceof XMLObject) {
|
||||
XMLObject xmlObject = (XMLObject)bound;
|
||||
xmlObject.ecmaPut(cx, id, value);
|
||||
} else {
|
||||
ScriptableObject.putConstProperty(bound, id, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the enumeration needed by the for..in statement.
|
||||
*
|
||||
|
@ -2862,17 +2874,23 @@ public class ScriptRuntime {
|
|||
|
||||
for (int i = varCount; i-- != 0;) {
|
||||
String name = funObj.getParamOrVarName(i);
|
||||
boolean isConst = funObj.getParamOrVarConst(i);
|
||||
// Don't overwrite existing def if already defined in object
|
||||
// or prototypes of object.
|
||||
if (!ScriptableObject.hasProperty(scope, name)) {
|
||||
if (!evalScript) {
|
||||
// Global var definitions are supposed to be DONTDELETE
|
||||
ScriptableObject.defineProperty(
|
||||
varScope, name, Undefined.instance,
|
||||
ScriptableObject.PERMANENT);
|
||||
if (isConst)
|
||||
ScriptableObject.defineConstProperty(varScope, name);
|
||||
else
|
||||
ScriptableObject.defineProperty(
|
||||
varScope, name, Undefined.instance,
|
||||
ScriptableObject.PERMANENT);
|
||||
} else {
|
||||
varScope.put(name, varScope, Undefined.instance);
|
||||
}
|
||||
} else {
|
||||
ScriptableObject.redefineProperty(scope, name, isConst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,8 @@ import org.mozilla.javascript.debug.DebuggableObject;
|
|||
*/
|
||||
|
||||
public abstract class ScriptableObject implements Scriptable, Serializable,
|
||||
DebuggableObject
|
||||
DebuggableObject,
|
||||
ConstProperties
|
||||
{
|
||||
|
||||
/**
|
||||
|
@ -106,6 +107,14 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
*/
|
||||
public static final int PERMANENT = 0x04;
|
||||
|
||||
/**
|
||||
* Property attribute indicating that this is a const property that has not
|
||||
* been assigned yet. The first 'const' assignment to the property will
|
||||
* clear this bit.
|
||||
*/
|
||||
public static final int UNINITIALIZED_CONST = 0x08;
|
||||
|
||||
public static final int CONST = PERMANENT|READONLY|UNINITIALIZED_CONST;
|
||||
/**
|
||||
* The prototype of this object.
|
||||
*/
|
||||
|
@ -138,6 +147,7 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
private static final int SLOT_MODIFY = 2;
|
||||
private static final int SLOT_REMOVE = 3;
|
||||
private static final int SLOT_MODIFY_GETTER_SETTER = 4;
|
||||
private static final int SLOT_MODIFY_CONST = 5;
|
||||
|
||||
private static class Slot implements Serializable
|
||||
{
|
||||
|
@ -202,7 +212,7 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
|
||||
static void checkValidAttributes(int attributes)
|
||||
{
|
||||
final int mask = READONLY | DONTENUM | PERMANENT;
|
||||
final int mask = READONLY | DONTENUM | PERMANENT | UNINITIALIZED_CONST;
|
||||
if ((attributes & ~mask) != 0) {
|
||||
throw new IllegalArgumentException(String.valueOf(attributes));
|
||||
}
|
||||
|
@ -298,7 +308,7 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
*/
|
||||
public void put(String name, Scriptable start, Object value)
|
||||
{
|
||||
if (putImpl(name, 0, start, value))
|
||||
if (putImpl(name, 0, start, value, EMPTY))
|
||||
return;
|
||||
|
||||
if (start == this) throw Kit.codeBug();
|
||||
|
@ -314,7 +324,7 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
*/
|
||||
public void put(int index, Scriptable start, Object value)
|
||||
{
|
||||
if (putImpl(null, index, start, value))
|
||||
if (putImpl(null, index, start, value, EMPTY))
|
||||
return;
|
||||
|
||||
if (start == this) throw Kit.codeBug();
|
||||
|
@ -349,6 +359,58 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
accessSlot(null, index, SLOT_REMOVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the named const property, creating it if need be.
|
||||
*
|
||||
* If the property was created using defineProperty, the
|
||||
* appropriate setter method is called. <p>
|
||||
*
|
||||
* If the property's attributes include READONLY, no action is
|
||||
* taken.
|
||||
* This method will actually set the property in the start
|
||||
* object.
|
||||
*
|
||||
* @param name the name of the property
|
||||
* @param start the object whose property is being set
|
||||
* @param value value to set the property to
|
||||
*/
|
||||
public void putConst(String name, Scriptable start, Object value)
|
||||
{
|
||||
if (putImpl(name, 0, start, value, READONLY))
|
||||
return;
|
||||
|
||||
if (start == this) throw Kit.codeBug();
|
||||
if (start instanceof ConstProperties)
|
||||
((ConstProperties)start).putConst(name, start, value);
|
||||
else
|
||||
start.put(name, start, value);
|
||||
}
|
||||
|
||||
public void defineConst(String name, Scriptable start)
|
||||
{
|
||||
if (putImpl(name, 0, start, Undefined.instance, UNINITIALIZED_CONST))
|
||||
return;
|
||||
|
||||
if (start == this) throw Kit.codeBug();
|
||||
if (start instanceof ConstProperties)
|
||||
((ConstProperties)start).defineConst(name, start);
|
||||
}
|
||||
/**
|
||||
* Returns true if the named property is defined as a const on this object.
|
||||
* @param name
|
||||
* @return true if the named property is defined as a const, false
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isConst(String name)
|
||||
{
|
||||
Slot slot = getSlot(name, 0, SLOT_QUERY);
|
||||
if (slot == null) {
|
||||
return false;
|
||||
}
|
||||
return (slot.getAttributes() & (PERMANENT|READONLY)) ==
|
||||
(PERMANENT|READONLY);
|
||||
|
||||
}
|
||||
/**
|
||||
* @deprecated Use {@link #getAttributes(String name)}. The engine always
|
||||
* ignored the start argument.
|
||||
|
@ -1136,6 +1198,22 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
so.defineProperty(propertyName, value, attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to add properties to arbitrary Scriptable object.
|
||||
* If destination is instance of ScriptableObject, calls
|
||||
* defineProperty there, otherwise calls put in destination
|
||||
* ignoring attributes
|
||||
*/
|
||||
public static void defineConstProperty(Scriptable destination,
|
||||
String propertyName)
|
||||
{
|
||||
if (destination instanceof ConstProperties) {
|
||||
ConstProperties cp = (ConstProperties)destination;
|
||||
cp.defineConst(propertyName, destination);
|
||||
} else
|
||||
defineProperty(destination, propertyName, Undefined.instance, CONST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a JavaScript property with getter and setter side effects.
|
||||
*
|
||||
|
@ -1516,6 +1594,30 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
return null != getBase(obj, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* If hasProperty(obj, name) would return true, then if the property that
|
||||
* was found is compatible with the new property, this method just returns.
|
||||
* If the property is not compatible, then an exception is thrown.
|
||||
*
|
||||
* A property redefinition is incompatible if the first definition was a
|
||||
* const declaration or if this one is. They are compatible only if neither
|
||||
* was const.
|
||||
*/
|
||||
public static void redefineProperty(Scriptable obj, String name,
|
||||
boolean isConst)
|
||||
{
|
||||
Scriptable base = getBase(obj, name);
|
||||
if (base == null)
|
||||
return;
|
||||
if (base instanceof ConstProperties) {
|
||||
ConstProperties cp = (ConstProperties)base;
|
||||
|
||||
if (cp.isConst(name))
|
||||
throw Context.reportRuntimeError1("msg.const.redecl", name);
|
||||
}
|
||||
if (isConst)
|
||||
throw Context.reportRuntimeError1("msg.var.redecl", name);
|
||||
}
|
||||
/**
|
||||
* Returns whether an indexed property is defined in an object or any object
|
||||
* in its prototype chain.
|
||||
|
@ -1537,11 +1639,11 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
* <p>
|
||||
* Searches for the named property in the prototype chain. If it is found,
|
||||
* the value of the property in <code>obj</code> is changed through a call
|
||||
* to {@link Scriptable#put(String, Scriptable, Object)} on the prototype
|
||||
* passing <code>obj</code> as the <code>start</code> argument. This allows
|
||||
* the prototype to veto the property setting in case the prototype defines
|
||||
* the property with [[ReadOnly]] attribute. If the property is not found,
|
||||
* it is added in <code>obj</code>.
|
||||
* to {@link Scriptable#put(String, Scriptable, Object)} on the
|
||||
* prototype passing <code>obj</code> as the <code>start</code> argument.
|
||||
* This allows the prototype to veto the property setting in case the
|
||||
* prototype defines the property with [[ReadOnly]] attribute. If the
|
||||
* property is not found, it is added in <code>obj</code>.
|
||||
* @param obj a JavaScript object
|
||||
* @param name a property name
|
||||
* @param value any JavaScript value accepted by Scriptable.put
|
||||
|
@ -1555,6 +1657,30 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
base.put(name, obj, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a named property in an object or in an object in its prototype chain.
|
||||
* <p>
|
||||
* Searches for the named property in the prototype chain. If it is found,
|
||||
* the value of the property in <code>obj</code> is changed through a call
|
||||
* to {@link Scriptable#put(String, Scriptable, Object)} on the
|
||||
* prototype passing <code>obj</code> as the <code>start</code> argument.
|
||||
* This allows the prototype to veto the property setting in case the
|
||||
* prototype defines the property with [[ReadOnly]] attribute. If the
|
||||
* property is not found, it is added in <code>obj</code>.
|
||||
* @param obj a JavaScript object
|
||||
* @param name a property name
|
||||
* @param value any JavaScript value accepted by Scriptable.put
|
||||
* @since 1.5R2
|
||||
*/
|
||||
public static void putConstProperty(Scriptable obj, String name, Object value)
|
||||
{
|
||||
Scriptable base = getBase(obj, name);
|
||||
if (base == null)
|
||||
base = obj;
|
||||
if (base instanceof ConstProperties)
|
||||
((ConstProperties)base).putConst(name, obj, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts an indexed property in an object or in an object in its prototype chain.
|
||||
* <p>
|
||||
|
@ -1842,8 +1968,19 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name
|
||||
* @param index
|
||||
* @param start
|
||||
* @param value
|
||||
* @param constFlag EMPTY means normal put. UNINITIALIZED_CONST means
|
||||
* defineConstProperty. READONLY means const initialization expression.
|
||||
* @return false if this != start and no slot was found. true if this == start
|
||||
* or this != start and a READONLY slot was found.
|
||||
*/
|
||||
private boolean putImpl(String name, int index, Scriptable start,
|
||||
Object value)
|
||||
Object value, int constFlag)
|
||||
{
|
||||
Slot slot;
|
||||
if (this != start) {
|
||||
|
@ -1853,6 +1990,20 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
}
|
||||
} else {
|
||||
checkNotSealed(name, index);
|
||||
// either const hoisted declaration or initialization
|
||||
if (constFlag != EMPTY) {
|
||||
slot = getSlot(name, index, SLOT_MODIFY_CONST);
|
||||
int attr = slot.getAttributes();
|
||||
if ((attr & READONLY) == 0)
|
||||
throw Context.reportRuntimeError1("msg.var.redecl", name);
|
||||
if ((attr & UNINITIALIZED_CONST) != 0) {
|
||||
slot.value = value;
|
||||
// clear the bit on const initialization
|
||||
if (constFlag != UNINITIALIZED_CONST)
|
||||
slot.setAttributes(attr & ~UNINITIALIZED_CONST);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
slot = getSlot(name, index, SLOT_MODIFY);
|
||||
}
|
||||
if ((slot.getAttributes() & READONLY) != 0)
|
||||
|
@ -1952,7 +2103,9 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
{
|
||||
int indexOrHash = (name != null ? name.hashCode() : index);
|
||||
|
||||
if (accessType == SLOT_QUERY || accessType == SLOT_MODIFY ||
|
||||
if (accessType == SLOT_QUERY ||
|
||||
accessType == SLOT_MODIFY ||
|
||||
accessType == SLOT_MODIFY_CONST ||
|
||||
accessType == SLOT_MODIFY_GETTER_SETTER)
|
||||
{
|
||||
// Check the hashtable without using synchronization
|
||||
|
@ -2003,6 +2156,9 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
} else if (accessType == SLOT_MODIFY_GETTER_SETTER) {
|
||||
if (slot instanceof GetterSlot)
|
||||
return slot;
|
||||
} else if (accessType == SLOT_MODIFY_CONST) {
|
||||
if (slot != null)
|
||||
return slot;
|
||||
} else {
|
||||
Kit.codeBug();
|
||||
}
|
||||
|
@ -2051,7 +2207,7 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
|
||||
if (slot != null) {
|
||||
// Another thread just added a slot with same
|
||||
// name/index before this one enetered synchronized
|
||||
// name/index before this one entered synchronized
|
||||
// block. This is a race in application code and
|
||||
// probably indicates bug there. But for the hashtable
|
||||
// implementation it is harmless with the only
|
||||
|
@ -2070,6 +2226,8 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
lastAccess = REMOVED;
|
||||
}
|
||||
slot = newSlot;
|
||||
} else if (accessType == SLOT_MODIFY_CONST) {
|
||||
return null;
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
@ -2087,6 +2245,8 @@ public abstract class ScriptableObject implements Scriptable, Serializable,
|
|||
Slot newSlot = (accessType == SLOT_MODIFY_GETTER_SETTER
|
||||
? new GetterSlot(name, indexOrHash, 0)
|
||||
: new Slot(name, indexOrHash, 0));
|
||||
if (accessType == SLOT_MODIFY_CONST)
|
||||
newSlot.setAttributes(CONST);
|
||||
++count;
|
||||
slotsLocalRef[insertPos] = newSlot;
|
||||
return newSlot;
|
||||
|
|
|
@ -247,7 +247,10 @@ public class Token
|
|||
|
||||
GET = 147, // JS 1.5 get pseudo keyword
|
||||
SET = 148, // JS 1.5 set pseudo keyword
|
||||
LAST_TOKEN = 148;
|
||||
CONST = 149,
|
||||
SETCONST = 150,
|
||||
SETCONSTVAR = 151,
|
||||
LAST_TOKEN = 152;
|
||||
|
||||
public static String name(int token)
|
||||
{
|
||||
|
@ -404,6 +407,8 @@ public class Token
|
|||
case TO_DOUBLE: return "TO_DOUBLE";
|
||||
case GET: return "GET";
|
||||
case SET: return "SET";
|
||||
case CONST: return "CONST";
|
||||
case SETCONST: return "SETCONST";
|
||||
}
|
||||
|
||||
// Token without name
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
* Mike McCabe
|
||||
* Igor Bukanov
|
||||
* Ethan Hugg
|
||||
* Bob Jervis
|
||||
* Terry Lucas
|
||||
* Milen Nankov
|
||||
*
|
||||
|
@ -151,7 +152,7 @@ class TokenStream
|
|||
Id_catch = Token.CATCH,
|
||||
Id_char = Token.RESERVED,
|
||||
Id_class = Token.RESERVED,
|
||||
Id_const = Token.RESERVED,
|
||||
Id_const = Token.CONST,
|
||||
Id_debugger = Token.RESERVED,
|
||||
Id_double = Token.RESERVED,
|
||||
Id_enum = Token.RESERVED,
|
||||
|
|
|
@ -669,7 +669,8 @@ public class Codegen extends Interpreter
|
|||
final int Do_getParamAndVarCount = 2;
|
||||
final int Do_getParamOrVarName = 3;
|
||||
final int Do_getEncodedSource = 4;
|
||||
final int SWITCH_COUNT = 5;
|
||||
final int Do_getParamOrVarConst = 5;
|
||||
final int SWITCH_COUNT = 6;
|
||||
|
||||
for (int methodIndex = 0; methodIndex != SWITCH_COUNT; ++methodIndex) {
|
||||
if (methodIndex == Do_getEncodedSource && encodedSource == null) {
|
||||
|
@ -703,6 +704,11 @@ public class Codegen extends Interpreter
|
|||
cfw.startMethod("getParamOrVarName", "(I)Ljava/lang/String;",
|
||||
ClassFileWriter.ACC_PUBLIC);
|
||||
break;
|
||||
case Do_getParamOrVarConst:
|
||||
metodLocals = 1 + 1 + 1; // this + paramOrVarName
|
||||
cfw.startMethod("getParamOrVarConst", "(I)Z",
|
||||
ClassFileWriter.ACC_PUBLIC);
|
||||
break;
|
||||
case Do_getEncodedSource:
|
||||
metodLocals = 1; // Only this
|
||||
cfw.startMethod("getEncodedSource", "()Ljava/lang/String;",
|
||||
|
@ -802,6 +808,43 @@ public class Codegen extends Interpreter
|
|||
}
|
||||
break;
|
||||
|
||||
case Do_getParamOrVarConst:
|
||||
// Push name of parameter using another switch
|
||||
// over paramAndVarCount
|
||||
paramAndVarCount = n.getParamAndVarCount();
|
||||
boolean [] constness = n.getParamAndVarConst();
|
||||
if (paramAndVarCount == 0) {
|
||||
// The runtime should never call the method in this
|
||||
// case but to make bytecode verifier happy return null
|
||||
// as throwing execption takes more code
|
||||
cfw.add(ByteCode.ICONST_0);
|
||||
cfw.add(ByteCode.IRETURN);
|
||||
} else if (paramAndVarCount == 1) {
|
||||
// As above do not check for valid index but always
|
||||
// return the name of the first param
|
||||
cfw.addPush(constness[0]);
|
||||
cfw.add(ByteCode.IRETURN);
|
||||
} else {
|
||||
// Do switch over getParamOrVarName
|
||||
cfw.addILoad(1); // param or var index
|
||||
// do switch from 1 .. paramAndVarCount - 1 mapping 0
|
||||
// to the default case
|
||||
int paramSwitchStart = cfw.addTableSwitch(
|
||||
1, paramAndVarCount - 1);
|
||||
for (int j = 0; j != paramAndVarCount; ++j) {
|
||||
if (cfw.getStackTop() != 0) Kit.codeBug();
|
||||
if (j == 0) {
|
||||
cfw.markTableSwitchDefault(paramSwitchStart);
|
||||
} else {
|
||||
cfw.markTableSwitchCase(paramSwitchStart, j - 1,
|
||||
0);
|
||||
}
|
||||
cfw.addPush(constness[j]);
|
||||
cfw.add(ByteCode.IRETURN);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Do_getEncodedSource:
|
||||
// Push number encoded source start and end
|
||||
// to prepare for encodedSource.substring(start, end)
|
||||
|
@ -1310,6 +1353,7 @@ class BodyCodegen
|
|||
|
||||
int paramCount = fnCurrent.fnode.getParamCount();
|
||||
int varCount = fnCurrent.fnode.getParamAndVarCount();
|
||||
boolean [] constDeclarations = fnCurrent.fnode.getParamAndVarConst();
|
||||
|
||||
// REMIND - only need to initialize the vars that don't get a value
|
||||
// before the next call and are used in the function
|
||||
|
@ -1325,11 +1369,11 @@ class BodyCodegen
|
|||
cfw.addAStore(reg);
|
||||
}
|
||||
} else if (fnCurrent.isNumberVar(i)) {
|
||||
reg = getNewWordPairLocal();
|
||||
reg = getNewWordPairLocal(constDeclarations[i]);
|
||||
cfw.addPush(0.0);
|
||||
cfw.addDStore(reg);
|
||||
} else {
|
||||
reg = getNewWordLocal();
|
||||
reg = getNewWordLocal(constDeclarations[i]);
|
||||
if (firstUndefVar == -1) {
|
||||
Codegen.pushUndefined(cfw);
|
||||
firstUndefVar = reg;
|
||||
|
@ -1339,6 +1383,10 @@ class BodyCodegen
|
|||
cfw.addAStore(reg);
|
||||
}
|
||||
if (reg >= 0) {
|
||||
if (constDeclarations[i]) {
|
||||
cfw.addPush(0);
|
||||
cfw.addIStore(reg + (fnCurrent.isNumberVar(i) ? 2 : 1));
|
||||
}
|
||||
varRegisters[i] = reg;
|
||||
}
|
||||
|
||||
|
@ -1654,6 +1702,11 @@ class BodyCodegen
|
|||
load's & pop's */
|
||||
visitSetVar(child, child.getFirstChild(), false);
|
||||
}
|
||||
else if (child.getType() == Token.SETCONSTVAR) {
|
||||
/* special case this so as to avoid unnecessary
|
||||
load's & pop's */
|
||||
visitSetConstVar(child, child.getFirstChild(), false);
|
||||
}
|
||||
else {
|
||||
generateExpression(child, node);
|
||||
if (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1)
|
||||
|
@ -2111,6 +2164,14 @@ class BodyCodegen
|
|||
visitSetName(node, child);
|
||||
break;
|
||||
|
||||
case Token.SETCONST:
|
||||
visitSetConst(node, child);
|
||||
break;
|
||||
|
||||
case Token.SETCONSTVAR:
|
||||
visitSetConstVar(node, child, true);
|
||||
break;
|
||||
|
||||
case Token.SETPROP:
|
||||
case Token.SETPROP_OP:
|
||||
visitSetProp(type, node, child);
|
||||
|
@ -3647,6 +3708,24 @@ Else pass the JS object in the aReg and 0.0 in the dReg.
|
|||
+")Ljava/lang/Object;");
|
||||
}
|
||||
|
||||
private void visitSetConst(Node node, Node child)
|
||||
{
|
||||
String name = node.getFirstChild().getString();
|
||||
while (child != null) {
|
||||
generateExpression(child, node);
|
||||
child = child.getNext();
|
||||
}
|
||||
cfw.addALoad(contextLocal);
|
||||
cfw.addPush(name);
|
||||
addScriptRuntimeInvoke(
|
||||
"setConst",
|
||||
"(Lorg/mozilla/javascript/Scriptable;"
|
||||
+"Ljava/lang/Object;"
|
||||
+"Lorg/mozilla/javascript/Context;"
|
||||
+"Ljava/lang/String;"
|
||||
+")Ljava/lang/Object;");
|
||||
}
|
||||
|
||||
private void visitGetVar(Node node)
|
||||
{
|
||||
if (!hasVarsInRegs) Kit.codeBug();
|
||||
|
@ -3676,7 +3755,16 @@ Else pass the JS object in the aReg and 0.0 in the dReg.
|
|||
generateExpression(child.getNext(), node);
|
||||
boolean isNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
|
||||
short reg = varRegisters[varIndex];
|
||||
if (varIsDirectCallParameter(varIndex)) {
|
||||
boolean [] constDeclarations = fnCurrent.fnode.getParamAndVarConst();
|
||||
if (constDeclarations[varIndex]) {
|
||||
if (!needValue) {
|
||||
if (isNumber)
|
||||
cfw.add(ByteCode.POP2);
|
||||
else
|
||||
cfw.add(ByteCode.POP);
|
||||
}
|
||||
}
|
||||
else if (varIsDirectCallParameter(varIndex)) {
|
||||
if (isNumber) {
|
||||
if (needValue) cfw.add(ByteCode.DUP2);
|
||||
cfw.addALoad(reg);
|
||||
|
@ -3701,8 +3789,8 @@ Else pass the JS object in the aReg and 0.0 in the dReg.
|
|||
}
|
||||
} else {
|
||||
if (isNumber) {
|
||||
cfw.addDStore(reg);
|
||||
if (needValue) cfw.addDLoad(reg);
|
||||
cfw.addDStore(reg);
|
||||
if (needValue) cfw.addDLoad(reg);
|
||||
}
|
||||
else {
|
||||
cfw.addAStore(reg);
|
||||
|
@ -3711,6 +3799,50 @@ Else pass the JS object in the aReg and 0.0 in the dReg.
|
|||
}
|
||||
}
|
||||
|
||||
private void visitSetConstVar(Node node, Node child, boolean needValue)
|
||||
{
|
||||
if (!hasVarsInRegs) Kit.codeBug();
|
||||
int varIndex = fnCurrent.getVarIndex(node);
|
||||
generateExpression(child.getNext(), node);
|
||||
boolean isNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
|
||||
short reg = varRegisters[varIndex];
|
||||
int beyond = cfw.acquireLabel();
|
||||
int noAssign = cfw.acquireLabel();
|
||||
if (isNumber) {
|
||||
cfw.addILoad(reg + 2);
|
||||
cfw.add(ByteCode.IFNE, noAssign);
|
||||
short stack = cfw.getStackTop();
|
||||
cfw.addPush(1);
|
||||
cfw.addIStore(reg + 2);
|
||||
cfw.addDStore(reg);
|
||||
if (needValue) {
|
||||
cfw.addDLoad(reg);
|
||||
cfw.markLabel(noAssign, stack);
|
||||
} else {
|
||||
cfw.add(ByteCode.GOTO, beyond);
|
||||
cfw.markLabel(noAssign, stack);
|
||||
cfw.add(ByteCode.POP2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
cfw.addILoad(reg + 1);
|
||||
cfw.add(ByteCode.IFNE, noAssign);
|
||||
short stack = cfw.getStackTop();
|
||||
cfw.addPush(1);
|
||||
cfw.addIStore(reg + 1);
|
||||
cfw.addAStore(reg);
|
||||
if (needValue) {
|
||||
cfw.addALoad(reg);
|
||||
cfw.markLabel(noAssign, stack);
|
||||
} else {
|
||||
cfw.add(ByteCode.GOTO, beyond);
|
||||
cfw.markLabel(noAssign, stack);
|
||||
cfw.add(ByteCode.POP);
|
||||
}
|
||||
}
|
||||
cfw.markLabel(beyond);
|
||||
}
|
||||
|
||||
private void visitGetProp(Node node, Node child)
|
||||
{
|
||||
generateExpression(child, node); //object
|
||||
|
@ -3987,20 +4119,45 @@ Else pass the JS object in the aReg and 0.0 in the dReg.
|
|||
addOptRuntimeInvoke("wrapDouble", "(D)Ljava/lang/Double;");
|
||||
}
|
||||
|
||||
private short getNewWordPairLocal()
|
||||
/**
|
||||
* Const locals use an extra slot to hold the has-been-assigned-once flag at
|
||||
* runtime.
|
||||
* @param isConst
|
||||
* @return
|
||||
*/
|
||||
private short getNewWordPairLocal(boolean isConst)
|
||||
{
|
||||
short result = firstFreeLocal;
|
||||
while (true) {
|
||||
if (result >= (MAX_LOCALS - 1))
|
||||
break;
|
||||
if (!locals[result]
|
||||
&& !locals[result + 1])
|
||||
break;
|
||||
result++;
|
||||
}
|
||||
short result = getConsecutiveSlots(2, isConst);
|
||||
if (result < (MAX_LOCALS - 1)) {
|
||||
locals[result] = true;
|
||||
locals[result + 1] = true;
|
||||
if (isConst)
|
||||
locals[result + 2] = true;
|
||||
if (result == firstFreeLocal) {
|
||||
for (int i = firstFreeLocal + 2; i < MAX_LOCALS; i++) {
|
||||
if (!locals[i]) {
|
||||
firstFreeLocal = (short) i;
|
||||
if (localsMax < firstFreeLocal)
|
||||
localsMax = firstFreeLocal;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
throw Context.reportRuntimeError("Program too complex " +
|
||||
"(out of locals)");
|
||||
}
|
||||
|
||||
private short getNewWordLocal(boolean isConst)
|
||||
{
|
||||
short result = getConsecutiveSlots(1, isConst);
|
||||
if (result < (MAX_LOCALS - 1)) {
|
||||
locals[result] = true;
|
||||
if (isConst)
|
||||
locals[result + 1] = true;
|
||||
if (result == firstFreeLocal) {
|
||||
for (int i = firstFreeLocal + 2; i < MAX_LOCALS; i++) {
|
||||
if (!locals[i]) {
|
||||
|
@ -4035,6 +4192,24 @@ Else pass the JS object in the aReg and 0.0 in the dReg.
|
|||
"(out of locals)");
|
||||
}
|
||||
|
||||
private short getConsecutiveSlots(int count, boolean isConst) {
|
||||
if (isConst)
|
||||
count++;
|
||||
short result = firstFreeLocal;
|
||||
while (true) {
|
||||
if (result >= (MAX_LOCALS - 1))
|
||||
break;
|
||||
int i;
|
||||
for (i = 0; i < count; i++)
|
||||
if (locals[result + i])
|
||||
break;
|
||||
if (i >= count)
|
||||
break;
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void releaseWordLocal(short local)
|
||||
{
|
||||
if (local < firstFreeLocal)
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* Bob Jervis
|
||||
* Roger Lawrence
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
|
@ -126,7 +127,8 @@ final class OptFunctionNode
|
|||
int type = n.getType();
|
||||
if (type == Token.GETVAR) {
|
||||
name = n.getString();
|
||||
} else if (type == Token.SETVAR) {
|
||||
} else if (type == Token.SETVAR ||
|
||||
type == Token.SETCONSTVAR) {
|
||||
name = n.getFirstChild().getString();
|
||||
} else {
|
||||
throw Kit.codeBug();
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#
|
||||
# Contributor(s):
|
||||
# Norris Boyd
|
||||
# Bob Jervis
|
||||
#
|
||||
# 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
|
||||
|
@ -245,12 +246,18 @@ msg.bad.regexp.compile =\
|
|||
msg.got.syntax.errors = \
|
||||
Compilation produced {0} syntax errors.
|
||||
|
||||
msg.var.redecl =\
|
||||
Redeclaration of var {0}.
|
||||
|
||||
msg.const.redecl =\
|
||||
Redeclaration of const {0}.
|
||||
|
||||
# NodeTransformer
|
||||
msg.dup.label =\
|
||||
duplicatet label
|
||||
duplicated label
|
||||
|
||||
msg.undef.label =\
|
||||
undefined labe
|
||||
undefined label
|
||||
|
||||
msg.bad.break =\
|
||||
unlabelled break must be inside loop or switch
|
||||
|
@ -532,7 +539,6 @@ msg.setter.parms =\
|
|||
msg.setter.bad.type =\
|
||||
Unsupported parameter type "{0}" in setter "{1}".
|
||||
|
||||
|
||||
msg.add.sealed =\
|
||||
Cannot add a property to a sealed object: {0}.
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче