Ignore:
Timestamp:
Oct 11, 2016, 4:25:38 PM (9 years ago)
Author:
[email protected]
Message:

Array.prototype.concat should not modify frozen objects.
https://bugs.webkit.org/show_bug.cgi?id=163302

Reviewed by Filip Pizlo.

JSTests:

  • stress/array-concat-on-frozen-object.js: Added.

Source/JavaScriptCore:

The ES6 spec for Array.prototype.concat states that it uses the
CreateDataPropertyOrThrow() to add items to the result array. The spec for
CreateDataPropertyOrThrow states:

"This abstract operation creates a property whose attributes are set to the same
defaults used for properties created by the ECMAScript language assignment
operator. Normally, the property will not already exist. If it does exist and is
not configurable or if O is not extensible, DefineOwnProperty will return
false causing this operation to throw a TypeError exception."

Since the properties of frozen objects are not extensible, not configurable, and
not writable, Array.prototype.concat should fail to write to the result array if
it is frozen.

Ref: https://tc39.github.io/ecma262/#sec-array.prototype.concat,
https://tc39.github.io/ecma262/#sec-createdatapropertyorthrow, and
https://tc39.github.io/ecma262/#sec-createdataproperty.

The fix consists of 2 parts:

  1. moveElement() should use the PutDirectIndexShouldThrow mode when invoking putDirectIndex(), and
  2. SparseArrayValueMap::putDirect() should check for the case where the property is read only.

(2) ensures that we don't write into a non-writable property.
(1) ensures that we throw a TypeError for attempts to write to a non-writeable
property.

  • runtime/ArrayPrototype.cpp:

(JSC::moveElements):

  • runtime/SparseArrayValueMap.cpp:

(JSC::SparseArrayValueMap::putDirect):

File:
1 edited

Legend:

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

    r207023 r207178  
    118118    SparseArrayEntry& entry = result.iterator->value;
    119119
    120     // To save a separate find & add, we first always add to the sparse map.
    121     // In the uncommon case that this is a new property, and the array is not
    122     // extensible, this is not the right thing to have done - so remove again.
    123     if (mode != PutDirectIndexLikePutDirect && result.isNewEntry && !array->isStructureExtensible()) {
    124         remove(result.iterator);
    125         return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible.");
     120    if (mode != PutDirectIndexLikePutDirect && !array->isStructureExtensible()) {
     121        // To save a separate find & add, we first always add to the sparse map.
     122        // In the uncommon case that this is a new property, and the array is not
     123        // extensible, this is not the right thing to have done - so remove again.
     124        if (result.isNewEntry) {
     125            remove(result.iterator);
     126            return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible.");
     127        }
     128        if (entry.attributes & ReadOnly)
     129            return reject(exec, mode == PutDirectIndexShouldThrow, ReadonlyPropertyWriteError);
    126130    }
    127 
    128131    entry.attributes = attributes;
    129132    entry.set(exec->vm(), this, value);
Note: See TracChangeset for help on using the changeset viewer.