Changeset 207226 in webkit


Ignore:
Timestamp:
Oct 12, 2016 11:27:50 AM (8 years ago)
Author:
mark.lam@apple.com
Message:

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

Reviewed by Filip Pizlo.

JSTests:

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

Source/JavaScriptCore:

  1. The ES6 spec for Array.prototype.slice (https://tc39.github.io/ecma262/#sec-array.prototype.slice) states that it uses the CreateDataPropertyOrThrow() (https://tc39.github.io/ecma262/#sec-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."

  1. Array.prototype.slice also uses a Set function (https://tc39.github.io/ecma262/#sec-set-o-p-v-throw) to set the "length" property and passes true for the Throw argument. Ultimately, it ends up calling the OrdinarySet function (https://tc39.github.io/ecma262/#sec-ordinaryset) that will fail if the property is not writable. This failure should result in a TypeError being thrown in Set.

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

If the source array being sliced has 1 or more elements, (1) will take effect
when we try to set the element in the non-writeable result obj.
If the source array being sliced has 0 elements, we will not set any elements and
(1) will not trigger. Subsequently, (2) will take effect when we will try to
set the length of the result obj.

  • runtime/ArrayPrototype.cpp:

(JSC::putLength):
(JSC::setLength):
(JSC::arrayProtoFuncSlice):
(JSC::arrayProtoFuncSplice):

Location:
trunk
Files:
1 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r207178 r207226  
     12016-10-12  Mark Lam  <mark.lam@apple.com>
     2
     3        Array.prototype.slice should not modify frozen objects.
     4        https://bugs.webkit.org/show_bug.cgi?id=163338
     5
     6        Reviewed by Filip Pizlo.
     7
     8        * stress/array-slice-on-frozen-object.js: Added.
     9
    1102016-10-11  Mark Lam  <mark.lam@apple.com>
    211
  • trunk/Source/JavaScriptCore/ChangeLog

    r207222 r207226  
     12016-10-12  Mark Lam  <mark.lam@apple.com>
     2
     3        Array.prototype.slice should not modify frozen objects.
     4        https://bugs.webkit.org/show_bug.cgi?id=163338
     5
     6        Reviewed by Filip Pizlo.
     7
     8        1. The ES6 spec for Array.prototype.slice
     9           (https://tc39.github.io/ecma262/#sec-array.prototype.slice) states that it uses
     10           the CreateDataPropertyOrThrow()
     11           (https://tc39.github.io/ecma262/#sec-createdatapropertyorthrow) to add items to
     12           the result array.  The spec for CreateDataPropertyOrThrow states:
     13
     14           "This abstract operation creates a property whose attributes are set to the
     15           same defaults used for properties created by the ECMAScript language assignment
     16           operator. Normally, the property will not already exist. If it does exist and
     17           is not configurable or if O is not extensible, [[DefineOwnProperty]] will
     18           return false causing this operation to throw a TypeError exception."
     19
     20        2. Array.prototype.slice also uses a Set function
     21           (https://tc39.github.io/ecma262/#sec-set-o-p-v-throw) to set the "length"
     22           property and passes true for the Throw argument.  Ultimately, it ends up
     23           calling the OrdinarySet function
     24           (https://tc39.github.io/ecma262/#sec-ordinaryset) that will fail if the
     25           property is not writable.  This failure should result in a TypeError being
     26           thrown in Set.
     27
     28           Since the properties of frozen objects are not extensible, not configurable,
     29           and not writeable, Array.prototype.slice should fail to write to the result
     30           array if it is frozen.
     31
     32        If the source array being sliced has 1 or more elements, (1) will take effect
     33        when we try to set the element in the non-writeable result obj.
     34        If the source array being sliced has 0 elements, we will not set any elements and
     35        (1) will not trigger.  Subsequently, (2) will take effect when we will try to
     36        set the length of the result obj.
     37
     38        * runtime/ArrayPrototype.cpp:
     39        (JSC::putLength):
     40        (JSC::setLength):
     41        (JSC::arrayProtoFuncSlice):
     42        (JSC::arrayProtoFuncSplice):
     43
    1442016-10-12  Filip Pizlo  <fpizlo@apple.com>
    245
  • trunk/Source/JavaScriptCore/runtime/ArrayPrototype.cpp

    r207178 r207226  
    164164}
    165165
    166 static ALWAYS_INLINE void putLength(ExecState* exec, VM& vm, JSObject* obj, JSValue value)
     166static ALWAYS_INLINE bool putLength(ExecState* exec, VM& vm, JSObject* obj, JSValue value)
    167167{
    168168    PutPropertySlot slot(obj);
    169     obj->methodTable()->put(obj, exec, vm.propertyNames->length, value, slot);
     169    return obj->methodTable()->put(obj, exec, vm.propertyNames->length, value, slot);
    170170}
    171171
    172172static ALWAYS_INLINE void setLength(ExecState* exec, VM& vm, JSObject* obj, unsigned value)
    173173{
    174     if (isJSArray(obj))
    175         jsCast<JSArray*>(obj)->setLength(exec, value);
    176     putLength(exec, vm, obj, jsNumber(value));
     174    auto scope = DECLARE_THROW_SCOPE(vm);
     175    static const bool throwException = true;
     176    if (isJSArray(obj)) {
     177        jsCast<JSArray*>(obj)->setLength(exec, value, throwException);
     178        RETURN_IF_EXCEPTION(scope, void());
     179    }
     180    bool success = putLength(exec, vm, obj, jsNumber(value));
     181    RETURN_IF_EXCEPTION(scope, void());
     182    if (UNLIKELY(!success))
     183        throwTypeError(exec, scope, ASCIILiteral(ReadonlyPropertyWriteError));
    177184}
    178185
     
    875882        RETURN_IF_EXCEPTION(scope, encodedJSValue());
    876883        if (v)
    877             result->putDirectIndex(exec, n, v);
    878     }
     884            result->putDirectIndex(exec, n, v, 0, PutDirectIndexShouldThrow);
     885    }
     886    scope.release();
    879887    setLength(exec, vm, result, n);
    880888    return JSValue::encode(result);
     
    908916
    909917        setLength(exec, vm, result, 0);
     918        RETURN_IF_EXCEPTION(scope, encodedJSValue());
     919        scope.release();
    910920        setLength(exec, vm, thisObj, length);
    911921        return JSValue::encode(result);
     
    973983    }
    974984   
     985    scope.release();
    975986    setLength(exec, vm, thisObj, length - deleteCount + additionalArgs);
    976987    return JSValue::encode(result);
Note: See TracChangeset for help on using the changeset viewer.