/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Norris Boyd * Igor Bukanov * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package org.mozilla.javascript; /** * This class implements the "arguments" object. * * See ECMA 10.1.8 * * @see org.mozilla.javascript.NativeCall * @author Norris Boyd */ final class Arguments extends IdScriptableObject { static final long serialVersionUID = 4275508002492040609L; private static final String FTAG = "Arguments"; public Arguments(NativeCall activation) { this.activation = activation; Scriptable parent = activation.getParentScope(); setParentScope(parent); setPrototype(ScriptableObject.getObjectPrototype(parent)); args = activation.originalArgs; lengthObj = Integer.valueOf(args.length); NativeFunction f = activation.function; calleeObj = f; Scriptable topLevel = getTopLevelScope(parent); objectCtor = (BaseFunction) getProperty(topLevel, "Object"); constructor = objectCtor; int version = f.getLanguageVersion(); if (version <= Context.VERSION_1_3 && version != Context.VERSION_DEFAULT) { callerObj = null; } else { callerObj = NOT_FOUND; } } @Override public String getClassName() { return FTAG; } private Object arg(int index) { if (index < 0 || args.length <= index) return NOT_FOUND; return args[index]; } // the following helper methods assume that 0 < index < args.length private void putIntoActivation(int index, Object value) { String argName = activation.function.getParamOrVarName(index); activation.put(argName, activation, value); } private Object getFromActivation(int index) { String argName = activation.function.getParamOrVarName(index); return activation.get(argName, activation); } private void replaceArg(int index, Object value) { if (sharedWithActivation(index)) { putIntoActivation(index, value); } synchronized (this) { if (args == activation.originalArgs) { args = args.clone(); } args[index] = value; } } private void removeArg(int index) { synchronized (this) { if (args[index] != NOT_FOUND) { if (args == activation.originalArgs) { args = args.clone(); } args[index] = NOT_FOUND; } } } // end helpers @Override public boolean has(int index, Scriptable start) { if (arg(index) != NOT_FOUND) { return true; } return super.has(index, start); } @Override public Object get(int index, Scriptable start) { final Object value = arg(index); if (value == NOT_FOUND) { return super.get(index, start); } else { if (sharedWithActivation(index)) { return getFromActivation(index); } else { return value; } } } private boolean sharedWithActivation(int index) { NativeFunction f = activation.function; int definedCount = f.getParamCount(); if (index < definedCount) { // Check if argument is not hidden by later argument with the same // name as hidden arguments are not shared with activation if (index < definedCount - 1) { String argName = f.getParamOrVarName(index); for (int i = index + 1; i < definedCount; i++) { if (argName.equals(f.getParamOrVarName(i))) { return false; } } } return true; } return false; } @Override public void put(int index, Scriptable start, Object value) { if (arg(index) == NOT_FOUND) { super.put(index, start, value); } else { replaceArg(index, value); } } @Override public void delete(int index) { if (0 <= index && index < args.length) { removeArg(index); } super.delete(index); } // #string_id_map# private static final int Id_callee = 1, Id_length = 2, Id_caller = 3, Id_constructor = 4, MAX_INSTANCE_ID = Id_constructor; @Override protected int getMaxInstanceId() { return MAX_INSTANCE_ID; } @Override protected int findInstanceIdInfo(String s) { int id; // #generated# Last update: 2010-01-06 05:48:21 ARST L0: { id = 0; String X = null; int c; int s_length = s.length(); if (s_length==6) { c=s.charAt(5); if (c=='e') { X="callee";id=Id_callee; } else if (c=='h') { X="length";id=Id_length; } else if (c=='r') { X="caller";id=Id_caller; } } else if (s_length==11) { X="constructor";id=Id_constructor; } if (X!=null && X!=s && !X.equals(s)) id = 0; break L0; } // #/generated# if (id == 0) return super.findInstanceIdInfo(s); int attr; switch (id) { case Id_callee: case Id_caller: case Id_length: case Id_constructor: attr = DONTENUM; break; default: throw new IllegalStateException(); } return instanceIdInfo(attr, id); } // #/string_id_map# @Override protected String getInstanceIdName(int id) { switch (id) { case Id_callee: return "callee"; case Id_length: return "length"; case Id_caller: return "caller"; case Id_constructor: return "constructor"; } return null; } @Override protected Object getInstanceIdValue(int id) { switch (id) { case Id_callee: return calleeObj; case Id_length: return lengthObj; case Id_caller: { Object value = callerObj; if (value == UniqueTag.NULL_VALUE) { value = null; } else if (value == null) { NativeCall caller = activation.parentActivationCall; if (caller != null) { value = caller.get("arguments", caller); } } return value; } case Id_constructor: return constructor; } return super.getInstanceIdValue(id); } @Override protected void setInstanceIdValue(int id, Object value) { switch (id) { case Id_callee: calleeObj = value; return; case Id_length: lengthObj = value; return; case Id_caller: callerObj = (value != null) ? value : UniqueTag.NULL_VALUE; return; case Id_constructor: constructor = value; return; } super.setInstanceIdValue(id, value); } @Override Object[] getIds(boolean getAll) { Object[] ids = super.getIds(getAll); if (args.length != 0) { boolean[] present = new boolean[args.length]; int extraCount = args.length; for (int i = 0; i != ids.length; ++i) { Object id = ids[i]; if (id instanceof Integer) { int index = ((Integer)id).intValue(); if (0 <= index && index < args.length) { if (!present[index]) { present[index] = true; extraCount--; } } } } if (!getAll) { // avoid adding args which were redefined to non-enumerable for (int i = 0; i < present.length; i++) { if (!present[i] && super.has(i, this)) { present[i] = true; extraCount--; } } } if (extraCount != 0) { Object[] tmp = new Object[extraCount + ids.length]; System.arraycopy(ids, 0, tmp, extraCount, ids.length); ids = tmp; int offset = 0; for (int i = 0; i != args.length; ++i) { if (present == null || !present[i]) { ids[offset] = Integer.valueOf(i); ++offset; } } if (offset != extraCount) Kit.codeBug(); } } return ids; } @Override protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { double d = ScriptRuntime.toNumber(id); int index = (int) d; if (d != index) { return super.getOwnPropertyDescriptor(cx, id); } Object value = arg(index); if (value == NOT_FOUND) { return super.getOwnPropertyDescriptor(cx, id); } if (sharedWithActivation(index)) { value = getFromActivation(index); } if (super.has(index, this)) { // the descriptor has been redefined ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id); desc.put("value", desc, value); return desc; } else { Scriptable scope = getParentScope(); if (scope == null) scope = this; return buildDataDescriptor(scope, value, EMPTY); } } @Override public void defineOwnProperty(Context cx, Object id, ScriptableObject desc) { super.defineOwnProperty(cx, id, desc); double d = ScriptRuntime.toNumber(id); int index = (int) d; if (d != index) return; Object value = arg(index); if (value == NOT_FOUND) return; if (isAccessorDescriptor(desc)) { removeArg(index); return; } Object newValue = getProperty(desc, "value"); if (newValue == NOT_FOUND) return; replaceArg(index, newValue); if (isFalse(getProperty(desc, "writable"))) { removeArg(index); } } // Fields to hold caller, callee and length properties, // where NOT_FOUND value tags deleted properties. // In addition if callerObj == NULL_VALUE, it tags null for scripts, as // initial callerObj == null means access to caller arguments available // only in JS <= 1.3 scripts private Object callerObj; private Object calleeObj; private Object lengthObj; private Object constructor; private NativeCall activation; private BaseFunction objectCtor; // Initially args holds activation.getOriginalArgs(), but any modification // of its elements triggers creation of a copy. If its element holds NOT_FOUND, // it indicates deleted index, in which case super class is queried. private Object[] args; }