1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 package org.mozilla.javascript; 8 9 /** 10 * This class implements the "arguments" object. 11 * 12 * See ECMA 10.1.8 13 * 14 * @see org.mozilla.javascript.NativeCall 15 * @author Norris Boyd 16 */ 17 final class Arguments extends IdScriptableObject 18 { 19 static final long serialVersionUID = 4275508002492040609L; 20 21 private static final String FTAG = "Arguments"; 22 Arguments(NativeCall activation)23 public Arguments(NativeCall activation) 24 { 25 this.activation = activation; 26 27 Scriptable parent = activation.getParentScope(); 28 setParentScope(parent); 29 setPrototype(ScriptableObject.getObjectPrototype(parent)); 30 31 args = activation.originalArgs; 32 lengthObj = Integer.valueOf(args.length); 33 34 NativeFunction f = activation.function; 35 calleeObj = f; 36 37 Scriptable topLevel = getTopLevelScope(parent); 38 constructor = getProperty(topLevel, "Object"); 39 40 int version = f.getLanguageVersion(); 41 if (version <= Context.VERSION_1_3 42 && version != Context.VERSION_DEFAULT) 43 { 44 callerObj = null; 45 } else { 46 callerObj = NOT_FOUND; 47 } 48 } 49 50 @Override getClassName()51 public String getClassName() 52 { 53 return FTAG; 54 } 55 arg(int index)56 private Object arg(int index) { 57 if (index < 0 || args.length <= index) return NOT_FOUND; 58 return args[index]; 59 } 60 61 // the following helper methods assume that 0 < index < args.length 62 putIntoActivation(int index, Object value)63 private void putIntoActivation(int index, Object value) { 64 String argName = activation.function.getParamOrVarName(index); 65 activation.put(argName, activation, value); 66 } 67 getFromActivation(int index)68 private Object getFromActivation(int index) { 69 String argName = activation.function.getParamOrVarName(index); 70 return activation.get(argName, activation); 71 } 72 replaceArg(int index, Object value)73 private void replaceArg(int index, Object value) { 74 if (sharedWithActivation(index)) { 75 putIntoActivation(index, value); 76 } 77 synchronized (this) { 78 if (args == activation.originalArgs) { 79 args = args.clone(); 80 } 81 args[index] = value; 82 } 83 } 84 removeArg(int index)85 private void removeArg(int index) { 86 synchronized (this) { 87 if (args[index] != NOT_FOUND) { 88 if (args == activation.originalArgs) { 89 args = args.clone(); 90 } 91 args[index] = NOT_FOUND; 92 } 93 } 94 } 95 96 // end helpers 97 98 @Override has(int index, Scriptable start)99 public boolean has(int index, Scriptable start) 100 { 101 if (arg(index) != NOT_FOUND) { 102 return true; 103 } 104 return super.has(index, start); 105 } 106 107 @Override get(int index, Scriptable start)108 public Object get(int index, Scriptable start) 109 { 110 final Object value = arg(index); 111 if (value == NOT_FOUND) { 112 return super.get(index, start); 113 } else { 114 if (sharedWithActivation(index)) { 115 return getFromActivation(index); 116 } else { 117 return value; 118 } 119 } 120 } 121 sharedWithActivation(int index)122 private boolean sharedWithActivation(int index) 123 { 124 NativeFunction f = activation.function; 125 int definedCount = f.getParamCount(); 126 if (index < definedCount) { 127 // Check if argument is not hidden by later argument with the same 128 // name as hidden arguments are not shared with activation 129 if (index < definedCount - 1) { 130 String argName = f.getParamOrVarName(index); 131 for (int i = index + 1; i < definedCount; i++) { 132 if (argName.equals(f.getParamOrVarName(i))) { 133 return false; 134 } 135 } 136 } 137 return true; 138 } 139 return false; 140 } 141 142 @Override put(int index, Scriptable start, Object value)143 public void put(int index, Scriptable start, Object value) 144 { 145 if (arg(index) == NOT_FOUND) { 146 super.put(index, start, value); 147 } else { 148 replaceArg(index, value); 149 } 150 } 151 152 @Override delete(int index)153 public void delete(int index) 154 { 155 if (0 <= index && index < args.length) { 156 removeArg(index); 157 } 158 super.delete(index); 159 } 160 161 // #string_id_map# 162 163 private static final int 164 Id_callee = 1, 165 Id_length = 2, 166 Id_caller = 3, 167 Id_constructor = 4, 168 169 MAX_INSTANCE_ID = Id_constructor; 170 171 @Override getMaxInstanceId()172 protected int getMaxInstanceId() 173 { 174 return MAX_INSTANCE_ID; 175 } 176 177 @Override findInstanceIdInfo(String s)178 protected int findInstanceIdInfo(String s) 179 { 180 int id; 181 // #generated# Last update: 2010-01-06 05:48:21 ARST 182 L0: { id = 0; String X = null; int c; 183 int s_length = s.length(); 184 if (s_length==6) { 185 c=s.charAt(5); 186 if (c=='e') { X="callee";id=Id_callee; } 187 else if (c=='h') { X="length";id=Id_length; } 188 else if (c=='r') { X="caller";id=Id_caller; } 189 } 190 else if (s_length==11) { X="constructor";id=Id_constructor; } 191 if (X!=null && X!=s && !X.equals(s)) id = 0; 192 break L0; 193 } 194 // #/generated# 195 196 if (id == 0) return super.findInstanceIdInfo(s); 197 198 int attr; 199 switch (id) { 200 case Id_callee: 201 case Id_caller: 202 case Id_length: 203 case Id_constructor: 204 attr = DONTENUM; 205 break; 206 default: throw new IllegalStateException(); 207 } 208 return instanceIdInfo(attr, id); 209 } 210 211 // #/string_id_map# 212 213 @Override getInstanceIdName(int id)214 protected String getInstanceIdName(int id) 215 { 216 switch (id) { 217 case Id_callee: return "callee"; 218 case Id_length: return "length"; 219 case Id_caller: return "caller"; 220 case Id_constructor: return "constructor"; 221 } 222 return null; 223 } 224 225 @Override getInstanceIdValue(int id)226 protected Object getInstanceIdValue(int id) 227 { 228 switch (id) { 229 case Id_callee: return calleeObj; 230 case Id_length: return lengthObj; 231 case Id_caller: { 232 Object value = callerObj; 233 if (value == UniqueTag.NULL_VALUE) { value = null; } 234 else if (value == null) { 235 NativeCall caller = activation.parentActivationCall; 236 if (caller != null) { 237 value = caller.get("arguments", caller); 238 } 239 } 240 return value; 241 } 242 case Id_constructor: 243 return constructor; 244 } 245 return super.getInstanceIdValue(id); 246 } 247 248 @Override setInstanceIdValue(int id, Object value)249 protected void setInstanceIdValue(int id, Object value) 250 { 251 switch (id) { 252 case Id_callee: calleeObj = value; return; 253 case Id_length: lengthObj = value; return; 254 case Id_caller: 255 callerObj = (value != null) ? value : UniqueTag.NULL_VALUE; 256 return; 257 case Id_constructor: constructor = value; return; 258 } 259 super.setInstanceIdValue(id, value); 260 } 261 262 @Override getIds(boolean getAll)263 Object[] getIds(boolean getAll) 264 { 265 Object[] ids = super.getIds(getAll); 266 if (args.length != 0) { 267 boolean[] present = new boolean[args.length]; 268 int extraCount = args.length; 269 for (int i = 0; i != ids.length; ++i) { 270 Object id = ids[i]; 271 if (id instanceof Integer) { 272 int index = ((Integer)id).intValue(); 273 if (0 <= index && index < args.length) { 274 if (!present[index]) { 275 present[index] = true; 276 extraCount--; 277 } 278 } 279 } 280 } 281 if (!getAll) { // avoid adding args which were redefined to non-enumerable 282 for (int i = 0; i < present.length; i++) { 283 if (!present[i] && super.has(i, this)) { 284 present[i] = true; 285 extraCount--; 286 } 287 } 288 } 289 if (extraCount != 0) { 290 Object[] tmp = new Object[extraCount + ids.length]; 291 System.arraycopy(ids, 0, tmp, extraCount, ids.length); 292 ids = tmp; 293 int offset = 0; 294 for (int i = 0; i != args.length; ++i) { 295 if (present == null || !present[i]) { 296 ids[offset] = Integer.valueOf(i); 297 ++offset; 298 } 299 } 300 if (offset != extraCount) Kit.codeBug(); 301 } 302 } 303 return ids; 304 } 305 306 @Override getOwnPropertyDescriptor(Context cx, Object id)307 protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) { 308 double d = ScriptRuntime.toNumber(id); 309 int index = (int) d; 310 if (d != index) { 311 return super.getOwnPropertyDescriptor(cx, id); 312 } 313 Object value = arg(index); 314 if (value == NOT_FOUND) { 315 return super.getOwnPropertyDescriptor(cx, id); 316 } 317 if (sharedWithActivation(index)) { 318 value = getFromActivation(index); 319 } 320 if (super.has(index, this)) { // the descriptor has been redefined 321 ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id); 322 desc.put("value", desc, value); 323 return desc; 324 } else { 325 Scriptable scope = getParentScope(); 326 if (scope == null) scope = this; 327 return buildDataDescriptor(scope, value, EMPTY); 328 } 329 } 330 331 @Override defineOwnProperty(Context cx, Object id, ScriptableObject desc, boolean checkValid)332 protected void defineOwnProperty(Context cx, Object id, 333 ScriptableObject desc, 334 boolean checkValid) { 335 super.defineOwnProperty(cx, id, desc, checkValid); 336 337 double d = ScriptRuntime.toNumber(id); 338 int index = (int) d; 339 if (d != index) return; 340 341 Object value = arg(index); 342 if (value == NOT_FOUND) return; 343 344 if (isAccessorDescriptor(desc)) { 345 removeArg(index); 346 return; 347 } 348 349 Object newValue = getProperty(desc, "value"); 350 if (newValue == NOT_FOUND) return; 351 352 replaceArg(index, newValue); 353 354 if (isFalse(getProperty(desc, "writable"))) { 355 removeArg(index); 356 } 357 } 358 359 // Fields to hold caller, callee and length properties, 360 // where NOT_FOUND value tags deleted properties. 361 // In addition if callerObj == NULL_VALUE, it tags null for scripts, as 362 // initial callerObj == null means access to caller arguments available 363 // only in JS <= 1.3 scripts 364 private Object callerObj; 365 private Object calleeObj; 366 private Object lengthObj; 367 private Object constructor; 368 369 private NativeCall activation; 370 371 // Initially args holds activation.getOriginalArgs(), but any modification 372 // of its elements triggers creation of a copy. If its element holds NOT_FOUND, 373 // it indicates deleted index, in which case super class is queried. 374 private Object[] args; 375 } 376