Introduction of generic Reference class to support latter constructions like f(args) = y and f(args)++. For now it is used to implement __proto__ and __parent__ special properties so x.__proto__++ now works

This commit is contained in:
igor%mir2.org 2004-05-31 07:18:52 +00:00
parent 086c591c10
commit 2f00186bd2
6 changed files with 515 additions and 351 deletions

View File

@ -375,6 +375,7 @@ class IRFactory
case Token.NAME:
case Token.GETPROP:
case Token.GETELEM:
case Token.GET_REF:
break;
case Token.VAR:
@ -841,7 +842,7 @@ class IRFactory
Node lvalueLeft = Node.newString(Token.BINDNAME, s);
return new Node(Token.SETNAME, lvalueLeft, op);
} else if (childType == Token.GETELEM) {
} else if (childType == Token.GETELEM || childType == Token.GET_REF) {
Node n = new Node(nodeType, childNode);
int type;
if (nodeType == Token.INC) {
@ -901,12 +902,11 @@ class IRFactory
}
}
if (special != 0) {
Node result = new Node(nodeType, left);
result.putIntProp(Node.SPECIAL_PROP_PROP, special);
return result;
} else {
checkActivationName(id, Token.GETPROP);
Node ref = new Node(Token.SPECIAL_REF, left);
ref.putIntProp(Node.SPECIAL_PROP_PROP, special);
return new Node(Token.GET_REF, ref);
}
checkActivationName(id, Token.GETPROP);
break;
case Token.LB:
@ -1070,17 +1070,15 @@ class IRFactory
int type;
if (nodeType == Token.GETPROP) {
type = Token.SETPROP;
int special = left.getIntProp(Node.SPECIAL_PROP_PROP, 0);
if (special != 0) {
Node result = new Node(Token.SETPROP, obj, right);
result.putIntProp(Node.SPECIAL_PROP_PROP, special);
return result;
}
} else {
type = Token.SETELEM;
}
return new Node(type, obj, id, right);
}
case Token.GET_REF: {
Node ref = left.getFirstChild();
return new Node(Token.SET_REF, ref, right);
}
default:
// TODO: This should be a ReferenceError--but that's a runtime
@ -1129,6 +1127,17 @@ class IRFactory
return new Node(type, obj, id, op);
}
case Token.GET_REF: {
Node ref = left.getFirstChild();
Node opLeft = new Node(Token.USE_STACK);
if (tonumber) {
opLeft = new Node(Token.POS, opLeft);
}
Node op = new Node(assignOp, opLeft, right);
return new Node(Token.SET_REF_OP, ref, op);
}
default:
// TODO: This should be a ReferenceError--but that's a runtime
// exception. Should we compile an exception into the code?

View File

@ -69,80 +69,75 @@ public class Interpreter
Icode_PROPDEC = -9,
Icode_VARDEC = -10,
Icode_ELEM_PRE_INC = -11,
Icode_ELEM_PRE_DEC = -12,
Icode_ELEM_POST_INC = -13,
Icode_ELEM_POST_DEC = -14,
Icode_ELEM_INC_DEC = -11,
Icode_REF_INC_DEC = -12,
// helper codes to deal with activation
Icode_SCOPE = -15,
Icode_TYPEOFNAME = -16,
Icode_SCOPE = -13,
Icode_TYPEOFNAME = -14,
// helper for function calls
Icode_NAME_FAST_THIS = -17,
Icode_NAME_SLOW_THIS = -18,
Icode_PUSH_PARENT = -19,
// Access to parent scope and prototype
Icode_GETPROTO = -20,
Icode_GETSCOPEPARENT = -21,
Icode_SETPROTO = -22,
Icode_SETPARENT = -23,
Icode_NAME_FAST_THIS = -15,
Icode_NAME_SLOW_THIS = -16,
Icode_PUSH_PARENT = -17,
// Create closure object for nested functions
Icode_CLOSURE = -24,
Icode_CLOSURE = -18,
// Special calls
Icode_CALLSPECIAL = -25,
Icode_CALLSPECIAL = -19,
// To return undefined value
Icode_RETUNDEF = -26,
Icode_RETUNDEF = -20,
// Exception handling implementation
Icode_CATCH = -27,
Icode_GOSUB = -28,
Icode_RETSUB = -29,
Icode_CATCH = -21,
Icode_GOSUB = -22,
Icode_RETSUB = -23,
// To indicating a line number change in icodes.
Icode_LINE = -30,
Icode_LINE = -24,
// To store shorts and ints inline
Icode_SHORTNUMBER = -31,
Icode_INTNUMBER = -32,
Icode_SHORTNUMBER = -25,
Icode_INTNUMBER = -26,
// To create and populate array to hold values for [] and {} literals
Icode_LITERAL_NEW = -33,
Icode_LITERAL_SET = -34,
Icode_LITERAL_NEW = -27,
Icode_LITERAL_SET = -28,
// Array literal with skipped index like [1,,2]
Icode_SPARE_ARRAYLIT = -35,
Icode_SPARE_ARRAYLIT = -29,
// Load index register to prepare for the following index operation
Icode_REG_IND_C0 = -36,
Icode_REG_IND_C1 = -37,
Icode_REG_IND_C2 = -38,
Icode_REG_IND_C3 = -39,
Icode_REG_IND_C4 = -40,
Icode_REG_IND_C5 = -41,
Icode_REG_IND1 = -42,
Icode_REG_IND2 = -43,
Icode_REG_IND4 = -44,
Icode_REG_IND_C0 = -30,
Icode_REG_IND_C1 = -31,
Icode_REG_IND_C2 = -32,
Icode_REG_IND_C3 = -33,
Icode_REG_IND_C4 = -34,
Icode_REG_IND_C5 = -35,
Icode_REG_IND1 = -36,
Icode_REG_IND2 = -37,
Icode_REG_IND4 = -38,
// Load string register to prepare for the following string operation
Icode_REG_STR_C0 = -45,
Icode_REG_STR_C1 = -46,
Icode_REG_STR_C2 = -47,
Icode_REG_STR_C3 = -48,
Icode_REG_STR1 = -49,
Icode_REG_STR2 = -50,
Icode_REG_STR4 = -51,
Icode_REG_STR_C0 = -39,
Icode_REG_STR_C1 = -40,
Icode_REG_STR_C2 = -41,
Icode_REG_STR_C3 = -42,
Icode_REG_STR1 = -43,
Icode_REG_STR2 = -44,
Icode_REG_STR4 = -45,
// Version of getvar/setvar that read var index directly from bytecode
Icode_GETVAR1 = -52,
Icode_SETVAR1 = -53,
Icode_GETVAR1 = -46,
Icode_SETVAR1 = -47,
// Construct special reference
Icode_SPECIAL_REF = -48,
// Last icode
MIN_ICODE = -53;
MIN_ICODE = -48;
static {
// Checks for byte code consistencies, good compiler can eliminate them
@ -157,16 +152,6 @@ public class Interpreter
System.err.println(str);
throw new IllegalStateException(str);
}
if (!(Icode_ELEM_PRE_INC - Node.PRE_INC == Icode_ELEM_PRE_INC
&& Icode_ELEM_PRE_INC - Node.PRE_DEC == Icode_ELEM_PRE_DEC
&& Icode_ELEM_PRE_INC - Node.POST_INC == Icode_ELEM_POST_INC
&& Icode_ELEM_PRE_INC - Node.POST_DEC == Icode_ELEM_POST_DEC))
{
String str = "Violation of pre/post mapping into elem bytecodes";
System.err.println(str);
throw new IllegalStateException(str);
}
}
private static boolean validIcode(int icode)
@ -695,12 +680,17 @@ public class Interpreter
case Token.GETPROP :
stackDelta = 1;
iCodeTop = visitGetProp(iCodeTop, node, child, false);
iCodeTop = visitGetProp(node, child, false, iCodeTop);
break;
case Token.GETELEM :
stackDelta = 1;
iCodeTop = visitGetElem(iCodeTop, node, child, false);
iCodeTop = visitGetElem(node, child, false, iCodeTop);
break;
case Token.GET_REF :
stackDelta = 1;
iCodeTop = visitGetRef(node, child, iCodeTop);
break;
case Token.DELPROP :
@ -754,45 +744,18 @@ public class Interpreter
stackDelta = 1;
iCodeTop = generateICode(child, iCodeTop);
child = child.getNext();
int special = node.getIntProp(Node.SPECIAL_PROP_PROP, 0);
if (special != 0) {
if (type == Token.SETPROP_OP) {
iCodeTop = addIcode(Icode_DUP, iCodeTop);
if (itsStackDepth > itsData.itsMaxStack)
itsData.itsMaxStack = itsStackDepth;
if (special == Node.SPECIAL_PROP_PROTO) {
iCodeTop = addIcode(Icode_GETPROTO, iCodeTop);
} else if (special == Node.SPECIAL_PROP_PARENT) {
iCodeTop = addIcode(Icode_GETSCOPEPARENT, iCodeTop);
} else {
throw badTree(node);
}
// Compensate for the following USE_STACK
itsStackDepth--;
}
iCodeTop = generateICode(child, iCodeTop);
if (special == Node.SPECIAL_PROP_PROTO) {
iCodeTop = addIcode(Icode_SETPROTO, iCodeTop);
} else if (special == Node.SPECIAL_PROP_PARENT) {
iCodeTop = addIcode(Icode_SETPARENT, iCodeTop);
} else {
throw badTree(node);
}
itsStackDepth--;
} else {
String property = child.getString();
child = child.getNext();
if (type == Token.SETPROP_OP) {
iCodeTop = addIcode(Icode_DUP, iCodeTop);
stackChange(1);
iCodeTop = addStringOp(Token.GETPROP, property, iCodeTop);
// Compensate for the following USE_STACK
stackChange(-1);
}
iCodeTop = generateICode(child, iCodeTop);
iCodeTop = addStringOp(Token.SETPROP, property, iCodeTop);
String property = child.getString();
child = child.getNext();
if (type == Token.SETPROP_OP) {
iCodeTop = addIcode(Icode_DUP, iCodeTop);
stackChange(1);
iCodeTop = addStringOp(Token.GETPROP, property, iCodeTop);
// Compensate for the following USE_STACK
stackChange(-1);
}
iCodeTop = generateICode(child, iCodeTop);
iCodeTop = addStringOp(Token.SETPROP, property, iCodeTop);
stackChange(-1);
break;
}
@ -816,6 +779,23 @@ public class Interpreter
stackChange(-2);
break;
case Token.SET_REF :
case Token.SET_REF_OP :
stackDelta = 1;
iCodeTop = generateICode(child, iCodeTop);
child = child.getNext();
if (type == Token.SET_REF_OP) {
iCodeTop = addIcode(Icode_DUP, iCodeTop);
stackChange(1);
iCodeTop = addToken(Token.GET_REF, iCodeTop);
// Compensate for the following USE_STACK
stackChange(-1);
}
iCodeTop = generateICode(child, iCodeTop);
iCodeTop = addToken(Token.SET_REF, iCodeTop);
stackChange(-1);
break;
case Token.SETNAME :
stackDelta = 1;
iCodeTop = generateICode(child, iCodeTop);
@ -1067,9 +1047,18 @@ public class Interpreter
case Token.ARRAYLIT:
case Token.OBJECTLIT:
stackDelta = 1;
iCodeTop = visitLiteral(iCodeTop, node, child);
iCodeTop = visitLiteral(node, child, iCodeTop);
break;
case Token.SPECIAL_REF: {
stackDelta = 1;
iCodeTop = generateICode(child, iCodeTop);
int special = node.getExistingIntProp(Node.SPECIAL_PROP_PROP);
iCodeTop = addIcode(Icode_SPECIAL_REF, iCodeTop);
iCodeTop = addByte(special, iCodeTop);
break;
}
default :
throw badTree(node);
}
@ -1110,13 +1099,13 @@ public class Interpreter
case Token.GETPROP:
// x.y(...)
// -> tmp = x, (tmp.y, tmp)(...)
iCodeTop = visitGetProp(iCodeTop, left, left.getFirstChild(), true);
iCodeTop = visitGetProp(left, left.getFirstChild(), true, iCodeTop);
iCodeTop = addIcode(Icode_SWAP, iCodeTop);
break;
case Token.GETELEM:
// x[y](...)
// -> tmp = x, (tmp[y], tmp)(...)
iCodeTop = visitGetElem(iCodeTop, left, left.getFirstChild(), true);
iCodeTop = visitGetElem(left, left.getFirstChild(), true, iCodeTop);
iCodeTop = addIcode(Icode_SWAP, iCodeTop);
break;
default:
@ -1129,31 +1118,22 @@ public class Interpreter
return iCodeTop;
}
private int visitGetProp(int iCodeTop, Node node, Node child, boolean dupObject)
private int visitGetProp(Node node, Node child, boolean dupObject,
int iCodeTop)
{
iCodeTop = generateICode(child, iCodeTop);
if (dupObject) {
iCodeTop = addIcode(Icode_DUP, iCodeTop);
stackChange(1);
}
int special = node.getIntProp(Node.SPECIAL_PROP_PROP, 0);
if (special != 0) {
if (special == Node.SPECIAL_PROP_PROTO) {
iCodeTop = addIcode(Icode_GETPROTO, iCodeTop);
} else if (special == Node.SPECIAL_PROP_PARENT) {
iCodeTop = addIcode(Icode_GETSCOPEPARENT, iCodeTop);
} else {
throw badTree(node);
}
} else {
child = child.getNext();
String property = child.getString();
iCodeTop = addStringOp(Token.GETPROP, property, iCodeTop);
}
child = child.getNext();
String property = child.getString();
iCodeTop = addStringOp(Token.GETPROP, property, iCodeTop);
return iCodeTop;
}
private int visitGetElem(int iCodeTop, Node node, Node child, boolean dupObject)
private int visitGetElem(Node node, Node child, boolean dupObject,
int iCodeTop)
{
iCodeTop = generateICode(child, iCodeTop);
if (dupObject) {
@ -1167,6 +1147,13 @@ public class Interpreter
return iCodeTop;
}
private int visitGetRef(Node node, Node child, int iCodeTop)
{
iCodeTop = generateICode(child, iCodeTop);
iCodeTop = addToken(Token.GET_REF, iCodeTop);
return iCodeTop;
}
private int visitIncDec(int iCodeTop, Node node, Node child)
{
int type = node.getType();
@ -1199,16 +1186,24 @@ public class Interpreter
break;
}
case Token.GETELEM : {
int incrDecrType = node.getExistingIntProp(Node.INCRDECR_PROP);
Node object = child.getFirstChild();
iCodeTop = generateICode(object, iCodeTop);
Node index = object.getNext();
iCodeTop = generateICode(index, iCodeTop);
int op = Icode_ELEM_PRE_INC
- node.getExistingIntProp(Node.INCRDECR_PROP);
iCodeTop = addIcode(op, iCodeTop);
iCodeTop = addIcode(Icode_ELEM_INC_DEC, iCodeTop);
iCodeTop = addByte(incrDecrType, iCodeTop);
stackChange(-1);
break;
}
case Token.GET_REF : {
int incrDecrType = node.getExistingIntProp(Node.INCRDECR_PROP);
Node ref = child.getFirstChild();
iCodeTop = generateICode(ref, iCodeTop);
iCodeTop = addIcode(Icode_REF_INC_DEC, iCodeTop);
iCodeTop = addByte(incrDecrType, iCodeTop);
break;
}
default : {
iCodeTop = addStringOp(type == Token.INC
? Icode_NAMEINC
@ -1223,7 +1218,7 @@ public class Interpreter
return iCodeTop;
}
private int visitLiteral(int iCodeTop, Node node, Node child)
private int visitLiteral(Node node, Node child, int iCodeTop)
{
int type = node.getType();
int count;
@ -1618,19 +1613,13 @@ public class Interpreter
case Icode_NAMEDEC: return "NAMEDEC";
case Icode_PROPDEC: return "PROPDEC";
case Icode_VARDEC: return "VARDEC";
case Icode_ELEM_PRE_INC: return "ELEM_PRE_INC";
case Icode_ELEM_PRE_DEC: return "ELEM_PRE_DEC";
case Icode_ELEM_POST_INC: return "ELEM_POST_INC";
case Icode_ELEM_POST_DEC: return "ELEM_POST_DEC";
case Icode_ELEM_INC_DEC: return "ELEM_INC_DEC";
case Icode_REF_INC_DEC: return "REF_INC_DEC";
case Icode_SCOPE: return "SCOPE";
case Icode_TYPEOFNAME: return "TYPEOFNAME";
case Icode_NAME_FAST_THIS: return "NAME_FAST_THIS";
case Icode_NAME_SLOW_THIS: return "NAME_SLOW_THIS";
case Icode_GETPROTO: return "GETPROTO";
case Icode_PUSH_PARENT: return "PUSH_PARENT";
case Icode_GETSCOPEPARENT: return "GETSCOPEPARENT";
case Icode_SETPROTO: return "SETPROTO";
case Icode_SETPARENT: return "SETPARENT";
case Icode_CLOSURE: return "CLOSURE";
case Icode_CALLSPECIAL: return "CALLSPECIAL";
case Icode_RETUNDEF: return "RETUNDEF";
@ -1661,6 +1650,7 @@ public class Interpreter
case Icode_REG_STR4: return "LOAD_STR4";
case Icode_GETVAR1: return "GETVAR1";
case Icode_SETVAR1: return "SETVAR1";
case Icode_SPECIAL_REF: return "SPECIAL_REF";
}
// icode without name
@ -1706,6 +1696,21 @@ public class Interpreter
pc += 2;
break;
}
case Icode_ELEM_INC_DEC :
case Icode_REF_INC_DEC: {
int incrDecrType = iCode[pc];
out.println(tname + " " + incrDecrType);
++pc;
break;
}
case Icode_SPECIAL_REF : {
int specialType = iCode[pc];
out.println(tname + " " + specialType);
++pc;
break;
}
case Icode_CALLSPECIAL : {
int callType = iCode[pc] & 0xFF;
boolean isNew = (iCode[pc + 1] != 0);
@ -1855,6 +1860,15 @@ public class Interpreter
// index of potential function name for debugging
return 1 + 2;
case Icode_ELEM_INC_DEC:
case Icode_REF_INC_DEC:
// type of ++/--
return 1 + 1;
case Icode_SPECIAL_REF:
// type of special property
return 1 + 1;
case Icode_SHORTNUMBER :
// short number
return 1 + 2;
@ -2482,25 +2496,46 @@ switch (op) {
case Token.SETELEM :
stackTop = do_setElem(stack, sDbl, stackTop, cx, scope);
continue Loop;
case Icode_PROPINC :
case Icode_PROPDEC : {
Object lhs = stack[stackTop];
if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);
stack[stackTop] = ScriptRuntime.postIncrDecr(lhs, stringReg, scope,
op == Icode_PROPINC);
continue Loop;
}
case Icode_ELEM_PRE_INC:
case Icode_ELEM_PRE_DEC:
case Icode_ELEM_POST_INC:
case Icode_ELEM_POST_DEC: {
case Icode_ELEM_INC_DEC: {
Object rhs = stack[stackTop];
if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);
--stackTop;
Object lhs = stack[stackTop];
if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);
stack[stackTop] = ScriptRuntime.elemIncrDecr(lhs, rhs, scope,
Icode_ELEM_PRE_INC - op);
iCode[pc]);
++pc;
continue Loop;
}
case Token.GET_REF : {
Object lhs = stack[stackTop];
if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);
stack[stackTop] = ScriptRuntime.getReference(lhs);
continue Loop;
}
case Token.SET_REF : {
Object rhs = stack[stackTop];
if (rhs == DBL_MRK) rhs = doubleWrap(sDbl[stackTop]);
--stackTop;
Object lhs = stack[stackTop];
if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);
ScriptRuntime.setReference(lhs, rhs);
stack[stackTop] = rhs;
continue Loop;
}
case Icode_REF_INC_DEC : {
Object lhs = stack[stackTop];
if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);
stack[stackTop] = ScriptRuntime.referenceIncrDecr(lhs, iCode[pc]);
++pc;
continue Loop;
}
case Icode_PROPINC :
case Icode_PROPDEC : {
Object lhs = stack[stackTop];
if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);
stack[stackTop] = ScriptRuntime.postIncrDecr(lhs, stringReg, scope,
op == Icode_PROPINC);
continue Loop;
}
case Token.LOCAL_SAVE :
@ -2773,12 +2808,13 @@ switch (op) {
stack[++stackTop] = ScriptRuntime.getParent(lhs);
continue Loop;
}
case Icode_GETPROTO :
case Icode_GETSCOPEPARENT :
case Icode_SETPROTO :
case Icode_SETPARENT :
stackTop = do_specialProp(stack, sDbl, stackTop, op, scope);
case Icode_SPECIAL_REF : {
Object lhs = stack[stackTop];
if (lhs == DBL_MRK) lhs = doubleWrap(sDbl[stackTop]);
stack[stackTop] = ScriptRuntime.specialReference(lhs, scope, iCode[pc]);
++pc;
continue Loop;
}
case Icode_SCOPE :
stack[++stackTop] = scope;
continue Loop;
@ -3284,36 +3320,6 @@ switch (op) {
return stackTop;
}
private static int do_specialProp(Object[] stack, double[] sDbl,
int stackTop, int op, Scriptable scope)
{
Object val;
Object top = stack[stackTop];
if (top == DBL_MRK) top = doubleWrap(sDbl[stackTop]);
switch (op) {
default: throw Kit.codeBug();
case Icode_GETPROTO:
val = ScriptRuntime.getProto(top, scope);
break;
case Icode_GETSCOPEPARENT:
val = ScriptRuntime.getParent(top, scope);
break;
case Icode_SETPROTO:
case Icode_SETPARENT:
--stackTop;
Object snd = stack[stackTop];
if (snd == DBL_MRK) snd = doubleWrap(sDbl[stackTop]);
if (op == Icode_SETPROTO) {
val = ScriptRuntime.setProto(snd, top, scope);
} else {
val = ScriptRuntime.setParent(snd, top, scope);
}
break;
}
stack[stackTop] = val;
return stackTop;
}
static RuntimeException notAFunction(Object notAFunction,
InterpreterData idata, int namePC)
{

View File

@ -0,0 +1,55 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* 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 Reference class for Rhino.
*
* The Initial Developer of the Original Code is
* RUnit Software AS.
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s): Igor Bukanov, igor@fastmail.fm
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package org.mozilla.javascript;
/**
* Generic notion of callable object that can execute some script-related code
* upon request with specified values for script scope and this objects.
*/
public abstract class Reference
{
public abstract Object get();
public abstract void set(Object value);
public void delete()
{
}
}

View File

@ -1255,6 +1255,57 @@ public class ScriptRuntime {
return value;
}
public static Object getReference(Object obj)
{
if (!(obj instanceof Reference)) {
String msg = getMessage1("msg.no.ref.to.get", toString(obj));
throw constructError("ReferenceError", msg);
}
Reference ref = (Reference)obj;
return ref.get();
}
public static void setReference(Object obj, Object value)
{
if (!(obj instanceof Reference)) {
String msg = getMessage2("msg.no.ref.to.set",
toString(obj), toString(value));
throw constructError("ReferenceError", msg);
}
Reference ref = (Reference)obj;
ref.set(value);
}
public static Object specialReference(final Object obj,
final Scriptable scope,
final int specialType)
{
if (!(specialType == Node.SPECIAL_PROP_PROTO
|| specialType == Node.SPECIAL_PROP_PARENT))
{
throw Kit.codeBug();
}
return new Reference() {
public Object get()
{
if (specialType == Node.SPECIAL_PROP_PROTO) {
return getProto(obj, scope);
} else {
return getParent(obj, scope);
}
}
public void set(Object value)
{
if (specialType == Node.SPECIAL_PROP_PROTO) {
setProto(obj, value, scope);
} else {
setParent(obj, value, scope);
}
}
};
}
/**
* The delete operator
*
@ -1729,6 +1780,36 @@ public class ScriptRuntime {
}
}
public static Object referenceIncrDecr(Object obj, int type)
{
Object value = getReference(obj);
if (value == Undefined.instance)
return value;
boolean post = (type == Node.POST_INC || type == Node.POST_DEC);
double number;
if (value instanceof Number) {
number = ((Number)value).doubleValue();
} else {
number = toNumber(value);
if (post) {
// convert result to number
value = new Double(number);
}
}
if (type == Node.PRE_INC || type == Node.POST_INC) {
++number;
} else {
--number;
}
Number result = new Double(number);
setReference(obj, result);
if (post) {
return value;
} else {
return result;
}
}
public static Object toPrimitive(Object val) {
if (val == null || !(val instanceof Scriptable)) {
return val;

View File

@ -140,69 +140,73 @@ public class Token
RETURN_POPV = 66, // to return result stored as popv in functions
ARRAYLIT = 67, // array literal
OBJECTLIT = 68, // object literal
GET_REF = 69, // *reference
SET_REF = 70, // *reference = something
LAST_BYTECODE_TOKEN = 68,
LAST_BYTECODE_TOKEN = 70,
// End of interpreter bytecodes
TRY = 69,
SEMI = 70, // semicolon
LB = 71, // left and right brackets
RB = 72,
LC = 73, // left and right curlies (braces)
RC = 74,
LP = 75, // left and right parentheses
RP = 76,
COMMA = 77, // comma operator
ASSIGN = 78, // simple assignment (=)
ASSIGNOP = 79, // assignment with operation (+= -= etc.)
HOOK = 80, // conditional (?:)
COLON = 81,
OR = 82, // logical or (||)
AND = 83, // logical and (&&)
INC = 84, // increment/decrement (++ --)
DEC = 85,
DOT = 86, // member operator (.)
FUNCTION = 87, // function keyword
EXPORT = 88, // export keyword
IMPORT = 89, // import keyword
IF = 90, // if keyword
ELSE = 91, // else keyword
SWITCH = 92, // switch keyword
CASE = 93, // case keyword
DEFAULT = 94, // default keyword
WHILE = 95, // while keyword
DO = 96, // do keyword
FOR = 97, // for keyword
BREAK = 98, // break keyword
CONTINUE = 99, // continue keyword
VAR = 100, // var keyword
WITH = 101, // with keyword
CATCH = 102, // catch keyword
FINALLY = 103, // finally keyword
VOID = 104, // void keyword
RESERVED = 105, // reserved keywords
TRY = 71,
SEMI = 72, // semicolon
LB = 73, // left and right brackets
RB = 74,
LC = 75, // left and right curlies (braces)
RC = 76,
LP = 77, // left and right parentheses
RP = 78,
COMMA = 79, // comma operator
ASSIGN = 80, // simple assignment (=)
ASSIGNOP = 81, // assignment with operation (+= -= etc.)
HOOK = 82, // conditional (?:)
COLON = 83,
OR = 84, // logical or (||)
AND = 85, // logical and (&&)
INC = 86, // increment/decrement (++ --)
DEC = 87,
DOT = 88, // member operator (.)
FUNCTION = 89, // function keyword
EXPORT = 90, // export keyword
IMPORT = 91, // import keyword
IF = 92, // if keyword
ELSE = 93, // else keyword
SWITCH = 94, // switch keyword
CASE = 95, // case keyword
DEFAULT = 96, // default keyword
WHILE = 97, // while keyword
DO = 98, // do keyword
FOR = 99, // for keyword
BREAK = 100, // break keyword
CONTINUE = 101, // continue keyword
VAR = 102, // var keyword
WITH = 103, // with keyword
CATCH = 104, // catch keyword
FINALLY = 105, // finally keyword
VOID = 106, // void keyword
RESERVED = 107, // reserved keywords
EMPTY = 106,
EMPTY = 108,
/* types used for the parse tree - these never get returned
* by the scanner.
*/
BLOCK = 107, // statement block
LABEL = 108, // label
TARGET = 109,
LOOP = 110,
EXPRSTMT = 111,
JSR = 112,
SCRIPT = 113, // top-level node for entire script
TYPEOFNAME = 114, // for typeof(simple-name)
USE_STACK = 115,
SETPROP_OP = 116, // x.y op= something
SETELEM_OP = 117, // x[y] op= something
INIT_LIST = 118,
LOCAL_BLOCK = 119,
BLOCK = 109, // statement block
LABEL = 110, // label
TARGET = 111,
LOOP = 112,
EXPRSTMT = 113,
JSR = 114,
SCRIPT = 115, // top-level node for entire script
TYPEOFNAME = 116, // for typeof(simple-name)
USE_STACK = 117,
SETPROP_OP = 118, // x.y op= something
SETELEM_OP = 119, // x[y] op= something
INIT_LIST = 120,
LOCAL_BLOCK = 121,
SET_REF_OP = 122, // *reference op= something
SPECIAL_REF = 123, // reference for special properties like __proto
LAST_TOKEN = 119;
LAST_TOKEN = 123;
public static String name(int token)
{
@ -285,6 +289,8 @@ public class Token
case RETURN_POPV: return "RETURN_POPV";
case ARRAYLIT: return "ARRAYLIT";
case OBJECTLIT: return "OBJECTLIT";
case GET_REF: return "GET_REF";
case SET_REF: return "SET_REF";
case TRY: return "TRY";
case SEMI: return "SEMI";
case LB: return "LB";
@ -335,6 +341,8 @@ public class Token
case SETELEM_OP: return "SETELEM_OP";
case INIT_LIST: return "INIT_LIST";
case LOCAL_BLOCK: return "LOCAL_BLOCK";
case SET_REF_OP: return "SET_REF_OP";
case SPECIAL_REF: return "SPECIAL_REF";
}
// Token without name

View File

@ -1791,6 +1791,10 @@ class BodyCodegen
visitGetElem(node, child, false);
break;
case Token.GET_REF:
visitGetRef(node, child);
break;
case Token.GETVAR:
visitGetVar(node);
break;
@ -1809,62 +1813,14 @@ class BodyCodegen
break;
case Token.SETELEM:
case Token.SETELEM_OP: {
generateCodeFromNode(child, node);
child = child.getNext();
if (type == Token.SETELEM_OP) {
cfw.add(ByteCode.DUP);
}
generateCodeFromNode(child, node);
child = child.getNext();
boolean indexIsNumber
= (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
if (type == Token.SETELEM_OP) {
if (indexIsNumber) {
// stack: ... object object number
// -> ... object number object number
cfw.add(ByteCode.DUP2_X1);
cfw.addALoad(variableObjectLocal);
addOptRuntimeInvoke(
"getElem",
"(Ljava/lang/Object;D"
+"Lorg/mozilla/javascript/Scriptable;"
+")Ljava/lang/Object;");
} else {
// stack: ... object object indexObject
// -> ... object indexObject object indexObject
cfw.add(ByteCode.DUP_X1);
cfw.addALoad(variableObjectLocal);
addScriptRuntimeInvoke(
"getElem",
"(Ljava/lang/Object;"
+"Ljava/lang/Object;"
+"Lorg/mozilla/javascript/Scriptable;"
+")Ljava/lang/Object;");
}
}
generateCodeFromNode(child, node);
cfw.addALoad(variableObjectLocal);
if (indexIsNumber) {
addOptRuntimeInvoke(
"setElem",
"(Ljava/lang/Object;"
+"D"
+"Ljava/lang/Object;"
+"Lorg/mozilla/javascript/Scriptable;"
+")Ljava/lang/Object;");
}
else {
addScriptRuntimeInvoke(
"setElem",
"(Ljava/lang/Object;"
+"Ljava/lang/Object;"
+"Ljava/lang/Object;"
+"Lorg/mozilla/javascript/Scriptable;"
+")Ljava/lang/Object;");
}
case Token.SETELEM_OP:
visitSetElem(type, node, child);
break;
case Token.SET_REF:
case Token.SET_REF_OP:
visitSetRef(type, node, child);
break;
}
case Token.DELPROP:
cfw.addALoad(contextLocal);
@ -1889,6 +1845,10 @@ class BodyCodegen
cfw.addALoad(getLocalBlockRegister(node));
break;
case Token.SPECIAL_REF:
visitSpecialRef(node, child);
break;
default:
throw new RuntimeException("Unexpected node type "+type);
}
@ -2794,11 +2754,12 @@ class BodyCodegen
break;
}
case Token.GETELEM: {
int incrDecrType = node.getExistingIntProp(Node.INCRDECR_PROP);
Node getElemChild = child.getFirstChild();
generateCodeFromNode(getElemChild, node);
generateCodeFromNode(getElemChild.getNext(), node);
cfw.addALoad(variableObjectLocal);
cfw.addPush(node.getExistingIntProp(Node.INCRDECR_PROP));
cfw.addPush(incrDecrType);
addScriptRuntimeInvoke("elemIncrDecr",
"(Ljava/lang/Object;"
+"Ljava/lang/Object;"
@ -2806,6 +2767,15 @@ class BodyCodegen
+"I)Ljava/lang/Object;");
break;
}
case Token.GET_REF: {
int incrDecrType = node.getExistingIntProp(Node.INCRDECR_PROP);
Node refChild = child.getFirstChild();
generateCodeFromNode(refChild, node);
cfw.addPush(incrDecrType);
addScriptRuntimeInvoke(
"referenceIncrDecr", "(Ljava/lang/Object;I)Ljava/lang/Object;");
break;
}
default:
Codegen.badTree();
}
@ -3307,24 +3277,6 @@ class BodyCodegen
if (dupObject) {
cfw.add(ByteCode.DUP);
}
int special = node.getIntProp(Node.SPECIAL_PROP_PROP, 0);
if (special != 0) {
cfw.addALoad(variableObjectLocal);
String runtimeMethod;
if (special == Node.SPECIAL_PROP_PROTO) {
runtimeMethod = "getProto";
} else if (special == Node.SPECIAL_PROP_PARENT) {
runtimeMethod = "getParent";
} else {
throw Codegen.badTree();
}
addScriptRuntimeInvoke(
runtimeMethod,
"(Ljava/lang/Object;"
+"Lorg/mozilla/javascript/Scriptable;"
+")Lorg/mozilla/javascript/Scriptable;");
return;
}
Node nameChild = child.getNext();
generateCodeFromNode(nameChild, node); // the name
/*
@ -3375,49 +3327,18 @@ class BodyCodegen
}
}
private void visitGetRef(Node node, Node child)
{
generateCodeFromNode(child, node); // reference
addScriptRuntimeInvoke(
"getReference", "(Ljava/lang/Object;)Ljava/lang/Object;");
}
private void visitSetProp(int type, Node node, Node child)
{
Node objectChild = child;
generateCodeFromNode(child, node);
child = child.getNext();
int special = node.getIntProp(Node.SPECIAL_PROP_PROP, 0);
if (special != 0) {
if (type == Token.SETPROP_OP) {
cfw.add(ByteCode.DUP);
String runtimeMethod;
if (special == Node.SPECIAL_PROP_PROTO) {
runtimeMethod = "getProto";
} else if (special == Node.SPECIAL_PROP_PARENT) {
runtimeMethod = "getParent";
} else {
throw Codegen.badTree();
}
cfw.addALoad(variableObjectLocal);
addScriptRuntimeInvoke(
runtimeMethod,
"(Ljava/lang/Object;"
+"Lorg/mozilla/javascript/Scriptable;"
+")Lorg/mozilla/javascript/Scriptable;");
}
generateCodeFromNode(child, node);
cfw.addALoad(variableObjectLocal);
String runtimeMethod;
if (special == Node.SPECIAL_PROP_PROTO) {
runtimeMethod = "setProto";
} else if (special == Node.SPECIAL_PROP_PARENT) {
runtimeMethod = "setParent";
} else {
throw Codegen.badTree();
}
addScriptRuntimeInvoke(
runtimeMethod,
"(Ljava/lang/Object;"
+"Ljava/lang/Object;"
+"Lorg/mozilla/javascript/Scriptable;"
+")Ljava/lang/Object;");
return;
}
if (type == Token.SETPROP_OP) {
cfw.add(ByteCode.DUP);
}
@ -3459,6 +3380,78 @@ class BodyCodegen
+")Ljava/lang/Object;");
}
private void visitSetElem(int type, Node node, Node child)
{
generateCodeFromNode(child, node);
child = child.getNext();
if (type == Token.SETELEM_OP) {
cfw.add(ByteCode.DUP);
}
generateCodeFromNode(child, node);
child = child.getNext();
boolean indexIsNumber = (node.getIntProp(Node.ISNUMBER_PROP, -1) != -1);
if (type == Token.SETELEM_OP) {
if (indexIsNumber) {
// stack: ... object object number
// -> ... object number object number
cfw.add(ByteCode.DUP2_X1);
cfw.addALoad(variableObjectLocal);
addOptRuntimeInvoke(
"getElem",
"(Ljava/lang/Object;D"
+"Lorg/mozilla/javascript/Scriptable;"
+")Ljava/lang/Object;");
} else {
// stack: ... object object indexObject
// -> ... object indexObject object indexObject
cfw.add(ByteCode.DUP_X1);
cfw.addALoad(variableObjectLocal);
addScriptRuntimeInvoke(
"getElem",
"(Ljava/lang/Object;"
+"Ljava/lang/Object;"
+"Lorg/mozilla/javascript/Scriptable;"
+")Ljava/lang/Object;");
}
}
generateCodeFromNode(child, node);
cfw.addALoad(variableObjectLocal);
if (indexIsNumber) {
addOptRuntimeInvoke(
"setElem",
"(Ljava/lang/Object;"
+"D"
+"Ljava/lang/Object;"
+"Lorg/mozilla/javascript/Scriptable;"
+")Ljava/lang/Object;");
} else {
addScriptRuntimeInvoke(
"setElem",
"(Ljava/lang/Object;"
+"Ljava/lang/Object;"
+"Ljava/lang/Object;"
+"Lorg/mozilla/javascript/Scriptable;"
+")Ljava/lang/Object;");
}
}
private void visitSetRef(int type, Node node, Node child)
{
generateCodeFromNode(child, node);
child = child.getNext();
if (type == Token.SET_REF_OP) {
cfw.add(ByteCode.DUP);
addScriptRuntimeInvoke(
"getReference", "(Ljava/lang/Object;)Ljava/lang/Object;");
}
generateCodeFromNode(child, node);
cfw.add(ByteCode.DUP_X1);
// stack: value ref value
addScriptRuntimeInvoke(
"setReference", "(Ljava/lang/Object;Ljava/lang/Object;)V");
// stack: value
}
private void visitBind(Node node, Node child)
{
while (child != null) {
@ -3474,6 +3467,18 @@ class BodyCodegen
+")Lorg/mozilla/javascript/Scriptable;");
}
private void visitSpecialRef(Node node, Node child)
{
int special = node.getExistingIntProp(Node.SPECIAL_PROP_PROP);
generateCodeFromNode(child, node);
cfw.addALoad(variableObjectLocal); // get variable object
cfw.addPush(special); // push name
addScriptRuntimeInvoke("specialReference",
"(Ljava/lang/Object;"
+"Lorg/mozilla/javascript/Scriptable;"
+"I)Ljava/lang/Object;");
}
private int getLocalBlockRegister(Node node)
{
Node localBlock = (Node)node.getProp(Node.LOCAL_BLOCK_PROP);