1/* 2 * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) 3 * Copyright (C) 2001 Peter Kelly (pmk@post.com) 4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 5 * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) 6 * Copyright (C) 2007 Maks Orlovich 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 * 23 */ 24 25#include "config.h" 26#include "Arguments.h" 27 28#include "CopyVisitorInlines.h" 29#include "JSActivation.h" 30#include "JSArgumentsIterator.h" 31#include "JSFunction.h" 32#include "JSGlobalObject.h" 33#include "JSCInlines.h" 34 35using namespace std; 36 37namespace JSC { 38 39STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(Arguments); 40 41const ClassInfo Arguments::s_info = { "Arguments", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(Arguments) }; 42 43void Arguments::visitChildren(JSCell* cell, SlotVisitor& visitor) 44{ 45 Arguments* thisObject = jsCast<Arguments*>(cell); 46 ASSERT_GC_OBJECT_INHERITS(thisObject, info()); 47 COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); 48 ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); 49 JSObject::visitChildren(thisObject, visitor); 50 51 if (thisObject->m_registerArray) { 52 visitor.copyLater(thisObject, ArgumentsRegisterArrayCopyToken, 53 thisObject->m_registerArray.get(), thisObject->registerArraySizeInBytes()); 54 visitor.appendValues(thisObject->m_registerArray.get(), thisObject->m_numArguments); 55 } 56 if (thisObject->m_slowArgumentData) { 57 visitor.copyLater(thisObject, ArgumentsSlowArgumentDataCopyToken, 58 thisObject->m_slowArgumentData.get(), SlowArgumentData::sizeForNumArguments(thisObject->m_numArguments)); 59 } 60 visitor.append(&thisObject->m_callee); 61 visitor.append(&thisObject->m_activation); 62} 63 64void Arguments::copyBackingStore(JSCell* cell, CopyVisitor& visitor, CopyToken token) 65{ 66 Arguments* thisObject = jsCast<Arguments*>(cell); 67 ASSERT_GC_OBJECT_INHERITS(thisObject, info()); 68 69 70 switch (token) { 71 case ArgumentsRegisterArrayCopyToken: { 72 WriteBarrier<Unknown>* registerArray = thisObject->m_registerArray.get(); 73 if (!registerArray) 74 return; 75 76 if (visitor.checkIfShouldCopy(registerArray)) { 77 size_t bytes = thisObject->registerArraySizeInBytes(); 78 WriteBarrier<Unknown>* newRegisterArray = static_cast<WriteBarrier<Unknown>*>(visitor.allocateNewSpace(bytes)); 79 memcpy(newRegisterArray, registerArray, bytes); 80 thisObject->m_registerArray.setWithoutWriteBarrier(newRegisterArray); 81 thisObject->m_registers = newRegisterArray - CallFrame::offsetFor(1) - 1; 82 visitor.didCopy(registerArray, bytes); 83 } 84 return; 85 } 86 87 case ArgumentsSlowArgumentDataCopyToken: { 88 SlowArgumentData* slowArgumentData = thisObject->m_slowArgumentData.get(); 89 if (!slowArgumentData) 90 return; 91 92 if (visitor.checkIfShouldCopy(slowArgumentData)) { 93 size_t bytes = SlowArgumentData::sizeForNumArguments(thisObject->m_numArguments); 94 SlowArgumentData* newSlowArgumentData = static_cast<SlowArgumentData*>(visitor.allocateNewSpace(bytes)); 95 memcpy(newSlowArgumentData, slowArgumentData, bytes); 96 thisObject->m_slowArgumentData.setWithoutWriteBarrier(newSlowArgumentData); 97 visitor.didCopy(slowArgumentData, bytes); 98 } 99 return; 100 } 101 102 default: 103 return; 104 } 105} 106 107static EncodedJSValue JSC_HOST_CALL argumentsFuncIterator(ExecState*); 108 109void Arguments::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t copyLength, int32_t firstVarArgOffset) 110{ 111 uint32_t length = copyLength + firstVarArgOffset; 112 113 if (UNLIKELY(m_overrodeLength)) { 114 length = min(get(exec, exec->propertyNames().length).toUInt32(exec), length); 115 for (unsigned i = firstVarArgOffset; i < length; i++) 116 callFrame->setArgument(i, get(exec, i)); 117 return; 118 } 119 ASSERT(length == this->length(exec)); 120 for (size_t i = firstVarArgOffset; i < length; ++i) { 121 if (JSValue value = tryGetArgument(i)) 122 callFrame->setArgument(i - firstVarArgOffset, value); 123 else 124 callFrame->setArgument(i - firstVarArgOffset, get(exec, i)); 125 } 126} 127 128void Arguments::fillArgList(ExecState* exec, MarkedArgumentBuffer& args) 129{ 130 if (UNLIKELY(m_overrodeLength)) { 131 unsigned length = get(exec, exec->propertyNames().length).toUInt32(exec); 132 for (unsigned i = 0; i < length; i++) 133 args.append(get(exec, i)); 134 return; 135 } 136 uint32_t length = this->length(exec); 137 for (size_t i = 0; i < length; ++i) { 138 if (JSValue value = tryGetArgument(i)) 139 args.append(value); 140 else 141 args.append(get(exec, i)); 142 } 143} 144 145bool Arguments::getOwnPropertySlotByIndex(JSObject* object, ExecState* exec, unsigned i, PropertySlot& slot) 146{ 147 Arguments* thisObject = jsCast<Arguments*>(object); 148 if (JSValue value = thisObject->tryGetArgument(i)) { 149 slot.setValue(thisObject, None, value); 150 return true; 151 } 152 153 return JSObject::getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); 154} 155 156void Arguments::createStrictModeCallerIfNecessary(ExecState* exec) 157{ 158 if (m_overrodeCaller) 159 return; 160 161 VM& vm = exec->vm(); 162 m_overrodeCaller = true; 163 PropertyDescriptor descriptor; 164 descriptor.setAccessorDescriptor(globalObject()->throwTypeErrorGetterSetter(vm), DontEnum | DontDelete | Accessor); 165 methodTable(exec->vm())->defineOwnProperty(this, exec, vm.propertyNames->caller, descriptor, false); 166} 167 168void Arguments::createStrictModeCalleeIfNecessary(ExecState* exec) 169{ 170 if (m_overrodeCallee) 171 return; 172 173 VM& vm = exec->vm(); 174 m_overrodeCallee = true; 175 PropertyDescriptor descriptor; 176 descriptor.setAccessorDescriptor(globalObject()->throwTypeErrorGetterSetter(vm), DontEnum | DontDelete | Accessor); 177 methodTable(exec->vm())->defineOwnProperty(this, exec, vm.propertyNames->callee, descriptor, false); 178} 179 180bool Arguments::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) 181{ 182 Arguments* thisObject = jsCast<Arguments*>(object); 183 unsigned i = propertyName.asIndex(); 184 if (JSValue value = thisObject->tryGetArgument(i)) { 185 RELEASE_ASSERT(i < PropertyName::NotAnIndex); 186 slot.setValue(thisObject, None, value); 187 return true; 188 } 189 190 if (propertyName == exec->propertyNames().length && LIKELY(!thisObject->m_overrodeLength)) { 191 slot.setValue(thisObject, DontEnum, jsNumber(thisObject->m_numArguments)); 192 return true; 193 } 194 195 if (propertyName == exec->propertyNames().callee && LIKELY(!thisObject->m_overrodeCallee)) { 196 if (!thisObject->m_isStrictMode) { 197 slot.setValue(thisObject, DontEnum, thisObject->m_callee.get()); 198 return true; 199 } 200 thisObject->createStrictModeCalleeIfNecessary(exec); 201 } 202 203 if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode) 204 thisObject->createStrictModeCallerIfNecessary(exec); 205 206 if (JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot)) 207 return true; 208 if (propertyName == exec->propertyNames().iteratorPrivateName) { 209 VM& vm = exec->vm(); 210 JSGlobalObject* globalObject = exec->lexicalGlobalObject(); 211 thisObject->JSC_NATIVE_FUNCTION(exec->propertyNames().iteratorPrivateName, argumentsFuncIterator, DontEnum, 0); 212 if (JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot)) 213 return true; 214 } 215 return false; 216} 217 218void Arguments::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 219{ 220 Arguments* thisObject = jsCast<Arguments*>(object); 221 for (unsigned i = 0; i < thisObject->m_numArguments; ++i) { 222 if (!thisObject->isArgument(i)) 223 continue; 224 propertyNames.add(Identifier::from(exec, i)); 225 } 226 if (mode == IncludeDontEnumProperties) { 227 propertyNames.add(exec->propertyNames().callee); 228 propertyNames.add(exec->propertyNames().length); 229 } 230 JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode); 231} 232 233void Arguments::putByIndex(JSCell* cell, ExecState* exec, unsigned i, JSValue value, bool shouldThrow) 234{ 235 Arguments* thisObject = jsCast<Arguments*>(cell); 236 if (thisObject->trySetArgument(exec->vm(), i, value)) 237 return; 238 239 PutPropertySlot slot(thisObject, shouldThrow); 240 JSObject::put(thisObject, exec, Identifier::from(exec, i), value, slot); 241} 242 243void Arguments::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) 244{ 245 Arguments* thisObject = jsCast<Arguments*>(cell); 246 unsigned i = propertyName.asIndex(); 247 if (thisObject->trySetArgument(exec->vm(), i, value)) 248 return; 249 250 if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) { 251 thisObject->m_overrodeLength = true; 252 thisObject->putDirect(exec->vm(), propertyName, value, DontEnum); 253 return; 254 } 255 256 if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) { 257 if (!thisObject->m_isStrictMode) { 258 thisObject->m_overrodeCallee = true; 259 thisObject->putDirect(exec->vm(), propertyName, value, DontEnum); 260 return; 261 } 262 thisObject->createStrictModeCalleeIfNecessary(exec); 263 } 264 265 if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode) 266 thisObject->createStrictModeCallerIfNecessary(exec); 267 268 JSObject::put(thisObject, exec, propertyName, value, slot); 269} 270 271bool Arguments::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) 272{ 273 Arguments* thisObject = jsCast<Arguments*>(cell); 274 if (i < thisObject->m_numArguments) { 275 if (!Base::deletePropertyByIndex(cell, exec, i)) 276 return false; 277 if (thisObject->tryDeleteArgument(exec->vm(), i)) 278 return true; 279 } 280 return JSObject::deletePropertyByIndex(thisObject, exec, i); 281} 282 283bool Arguments::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) 284{ 285 if (exec->vm().isInDefineOwnProperty()) 286 return Base::deleteProperty(cell, exec, propertyName); 287 288 Arguments* thisObject = jsCast<Arguments*>(cell); 289 unsigned i = propertyName.asIndex(); 290 if (i < thisObject->m_numArguments) { 291 RELEASE_ASSERT(i < PropertyName::NotAnIndex); 292 if (!Base::deleteProperty(cell, exec, propertyName)) 293 return false; 294 if (thisObject->tryDeleteArgument(exec->vm(), i)) 295 return true; 296 } 297 298 if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) { 299 thisObject->m_overrodeLength = true; 300 return true; 301 } 302 303 if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) { 304 if (!thisObject->m_isStrictMode) { 305 thisObject->m_overrodeCallee = true; 306 return true; 307 } 308 thisObject->createStrictModeCalleeIfNecessary(exec); 309 } 310 311 if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode) 312 thisObject->createStrictModeCallerIfNecessary(exec); 313 314 return JSObject::deleteProperty(thisObject, exec, propertyName); 315} 316 317bool Arguments::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow) 318{ 319 Arguments* thisObject = jsCast<Arguments*>(object); 320 unsigned i = propertyName.asIndex(); 321 if (i < thisObject->m_numArguments) { 322 RELEASE_ASSERT(i < PropertyName::NotAnIndex); 323 // If the property is not yet present on the object, and is not yet marked as deleted, then add it now. 324 PropertySlot slot(thisObject); 325 if (!thisObject->isDeletedArgument(i) && !JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot)) { 326 JSValue value = thisObject->tryGetArgument(i); 327 ASSERT(value); 328 object->putDirectMayBeIndex(exec, propertyName, value); 329 } 330 if (!Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow)) 331 return false; 332 333 // From ES 5.1, 10.6 Arguments Object 334 // 5. If the value of isMapped is not undefined, then 335 if (thisObject->isArgument(i)) { 336 // a. If IsAccessorDescriptor(Desc) is true, then 337 if (descriptor.isAccessorDescriptor()) { 338 // i. Call the [[Delete]] internal method of map passing P, and false as the arguments. 339 thisObject->tryDeleteArgument(exec->vm(), i); 340 } else { // b. Else 341 // i. If Desc.[[Value]] is present, then 342 // 1. Call the [[Put]] internal method of map passing P, Desc.[[Value]], and Throw as the arguments. 343 if (descriptor.value()) 344 thisObject->trySetArgument(exec->vm(), i, descriptor.value()); 345 // ii. If Desc.[[Writable]] is present and its value is false, then 346 // 1. Call the [[Delete]] internal method of map passing P and false as arguments. 347 if (descriptor.writablePresent() && !descriptor.writable()) 348 thisObject->tryDeleteArgument(exec->vm(), i); 349 } 350 } 351 return true; 352 } 353 354 if (propertyName == exec->propertyNames().length && !thisObject->m_overrodeLength) { 355 thisObject->putDirect(exec->vm(), propertyName, jsNumber(thisObject->m_numArguments), DontEnum); 356 thisObject->m_overrodeLength = true; 357 } else if (propertyName == exec->propertyNames().callee && !thisObject->m_overrodeCallee) { 358 thisObject->putDirect(exec->vm(), propertyName, thisObject->m_callee.get(), DontEnum); 359 thisObject->m_overrodeCallee = true; 360 } else if (propertyName == exec->propertyNames().caller && thisObject->m_isStrictMode) 361 thisObject->createStrictModeCallerIfNecessary(exec); 362 363 return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow); 364} 365 366void Arguments::allocateRegisterArray(VM& vm) 367{ 368 ASSERT(!m_registerArray); 369 void* backingStore; 370 if (!vm.heap.tryAllocateStorage(this, registerArraySizeInBytes(), &backingStore)) 371 RELEASE_ASSERT_NOT_REACHED(); 372 m_registerArray.set(vm, this, static_cast<WriteBarrier<Unknown>*>(backingStore)); 373} 374 375void Arguments::tearOff(CallFrame* callFrame) 376{ 377 if (isTornOff()) 378 return; 379 380 if (!m_numArguments) 381 return; 382 383 // Must be called for the same call frame from which it was created. 384 ASSERT(bitwise_cast<WriteBarrier<Unknown>*>(callFrame) == m_registers); 385 386 allocateRegisterArray(callFrame->vm()); 387 m_registers = m_registerArray.get() - CallFrame::offsetFor(1) - 1; 388 389 // If we have a captured argument that logically aliases activation storage, 390 // but we optimize away the activation, the argument needs to tear off into 391 // our storage. The simplest way to do this is to revert it to Normal status. 392 if (m_slowArgumentData && !m_activation) { 393 for (size_t i = 0; i < m_numArguments; ++i) { 394 if (m_slowArgumentData->slowArguments()[i].status != SlowArgument::Captured) 395 continue; 396 m_slowArgumentData->slowArguments()[i].status = SlowArgument::Normal; 397 m_slowArgumentData->slowArguments()[i].index = CallFrame::argumentOffset(i); 398 } 399 } 400 401 for (size_t i = 0; i < m_numArguments; ++i) 402 trySetArgument(callFrame->vm(), i, callFrame->argumentAfterCapture(i)); 403} 404 405void Arguments::didTearOffActivation(ExecState* exec, JSActivation* activation) 406{ 407 RELEASE_ASSERT(activation); 408 if (isTornOff()) 409 return; 410 411 if (!m_numArguments) 412 return; 413 414 m_activation.set(exec->vm(), this, activation); 415 tearOff(exec); 416} 417 418void Arguments::tearOff(CallFrame* callFrame, InlineCallFrame* inlineCallFrame) 419{ 420 if (isTornOff()) 421 return; 422 423 if (!m_numArguments) 424 return; 425 426 allocateRegisterArray(callFrame->vm()); 427 m_registers = m_registerArray.get() - CallFrame::offsetFor(1) - 1; 428 429 for (size_t i = 0; i < m_numArguments; ++i) { 430 ValueRecovery& recovery = inlineCallFrame->arguments[i + 1]; 431 trySetArgument(callFrame->vm(), i, recovery.recover(callFrame)); 432 } 433} 434 435EncodedJSValue JSC_HOST_CALL argumentsFuncIterator(ExecState* exec) 436{ 437 JSObject* thisObj = exec->thisValue().toThis(exec, StrictMode).toObject(exec); 438 Arguments* arguments = jsDynamicCast<Arguments*>(thisObj); 439 if (!arguments) 440 return JSValue::encode(throwTypeError(exec, "Attempted to use Arguments iterator on non-Arguments object")); 441 return JSValue::encode(JSArgumentsIterator::create(exec->vm(), exec->callee()->globalObject()->argumentsIteratorStructure(), arguments)); 442} 443 444 445} // namespace JSC 446