Ignore:
Timestamp:
Feb 24, 2015, 9:08:06 AM (10 years ago)
Author:
[email protected]
Message:

Our bizarre behavior on Arguments::defineOwnProperty should be deliberate rather than a spaghetti incident
https://bugs.webkit.org/show_bug.cgi?id=141951

Reviewed by Benjamin Poulain.

This patch has no behavioral change, but it simplifies a bunch of wrong code. The code is
still wrong in exactly the same way, but at least it's obvious what's going on. The wrongness
is covered by this bug: https://bugs.webkit.org/show_bug.cgi?id=141952.

  • runtime/Arguments.cpp:

(JSC::Arguments::copyBackingStore): We should only see the arguments token; assert otherwise. This works because if the GC sees the butterfly token it calls the JSObject::copyBackingStore method directly.
(JSC::Arguments::defineOwnProperty): Make our bizarre behavior deliberate rather than an accident of a decade of patches.

  • tests/stress/arguments-bizarre-behavior.js: Added.

(foo):

  • tests/stress/arguments-bizarre-behaviour-disable-enumerability.js: Added. My choice of spellings of the word "behavio[u]r" is almost as consistent as our implementation of arguments.

(foo):

  • tests/stress/arguments-custom-properties-gc.js: Added. I added this test because at first I was unsure if we GCd arguments correctly.

(makeBaseArguments):
(makeArray):
(cons):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/Arguments.cpp

    r179887 r180564  
    8282
    8383    default:
    84         return;
     84        RELEASE_ASSERT_NOT_REACHED();
    8585    }
    8686}
     
    296296    if (i < thisObject->m_numArguments) {
    297297        RELEASE_ASSERT(i < PropertyName::NotAnIndex);
    298         // If the property is not yet present on the object, and is not yet marked as deleted, then add it now.
    299         PropertySlot slot(thisObject);
    300         if (!thisObject->isDeletedArgument(i) && !JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot)) {
     298       
     299        if (thisObject->isArgument(i)) {
     300            if (!descriptor.isAccessorDescriptor()) {
     301                // If the property is not deleted and we are using a non-accessor descriptor, then
     302                // make sure that the aliased argument sees the value.
     303                if (descriptor.value())
     304                    thisObject->trySetArgument(exec->vm(), i, descriptor.value());
     305           
     306                // If the property is not deleted and we are using a non-accessor, writable
     307                // descriptor, then we are done. The argument continues to be aliased. Note that we
     308                // ignore the request to change enumerability. We appear to have always done so, in
     309                // cases where the argument was still aliased.
     310                // FIXME: https://bugs.webkit.org/show_bug.cgi?id=141952
     311                if (descriptor.writable())
     312                    return true;
     313            }
     314           
     315            // If the property is a non-deleted argument, then move it into the base object and then
     316            // delete it.
    301317            JSValue value = thisObject->tryGetArgument(i);
    302318            ASSERT(value);
    303319            object->putDirectMayBeIndex(exec, propertyName, value);
    304         }
    305         if (!Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow))
    306             return false;
    307 
    308         // From ES 5.1, 10.6 Arguments Object
    309         // 5. If the value of isMapped is not undefined, then
    310         if (thisObject->isArgument(i)) {
    311             // a. If IsAccessorDescriptor(Desc) is true, then
    312             if (descriptor.isAccessorDescriptor()) {
    313                 // i. Call the [[Delete]] internal method of map passing P, and false as the arguments.
    314                 thisObject->tryDeleteArgument(exec->vm(), i);
    315             } else { // b. Else
    316                 // i. If Desc.[[Value]] is present, then
    317                 // 1. Call the [[Put]] internal method of map passing P, Desc.[[Value]], and Throw as the arguments.
    318                 if (descriptor.value())
    319                     thisObject->trySetArgument(exec->vm(), i, descriptor.value());
    320                 // ii. If Desc.[[Writable]] is present and its value is false, then
    321                 // 1. Call the [[Delete]] internal method of map passing P and false as arguments.
    322                 if (descriptor.writablePresent() && !descriptor.writable())
    323                     thisObject->tryDeleteArgument(exec->vm(), i);
    324             }
    325         }
    326         return true;
     320            thisObject->tryDeleteArgument(exec->vm(), i);
     321        }
     322       
     323        // Now just let the normal object machinery do its thing.
     324        return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow);
    327325    }
    328326
Note: See TracChangeset for help on using the changeset viewer.