Ignore:
Timestamp:
Feb 26, 2016, 12:25:42 PM (9 years ago)
Author:
[email protected]
Message:

Native Typed Array functions should use Symbol.species
https://bugs.webkit.org/show_bug.cgi?id=154569

Reviewed by Michael Saboff.

This patch adds support for Symbol.species in the native Typed Array prototype
functions. Additionally, now that other types of typedarrays are creatable inside
the slice we use the JSGenericTypedArrayView::set function, which has been beefed
up, to put everything into the correct place.

  • runtime/JSDataView.cpp:

(JSC::JSDataView::set):

  • runtime/JSDataView.h:
  • runtime/JSGenericTypedArrayView.h:
  • runtime/JSGenericTypedArrayViewConstructorInlines.h:

(JSC::constructGenericTypedArrayViewFromIterator):
(JSC::constructGenericTypedArrayViewWithArguments):
(JSC::constructGenericTypedArrayView):

  • runtime/JSGenericTypedArrayViewInlines.h:

(JSC::JSGenericTypedArrayView<Adaptor>::setWithSpecificType):
(JSC::JSGenericTypedArrayView<Adaptor>::set):

  • runtime/JSGenericTypedArrayViewPrototypeFunctions.h:

(JSC::speciesConstruct):
(JSC::genericTypedArrayViewProtoFuncSet):
(JSC::genericTypedArrayViewProtoFuncSlice):
(JSC::genericTypedArrayViewProtoFuncSubarray):

  • tests/stress/typedarray-slice.js:

(subclasses.typedArrays.map):
(testSpecies):
(forEach):
(subclasses.forEach):
(testSpeciesRemoveConstructor):
(testSpeciesWithSameBuffer):

  • tests/stress/typedarray-subarray.js: Added.

(subclasses.typedArrays.map):
(testSpecies):
(forEach):
(subclasses.forEach):
(testSpeciesRemoveConstructor):

Location:
trunk/Source/JavaScriptCore/runtime
Files:
6 edited

Legend:

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

    r191215 r197192  
    7979}
    8080
    81 bool JSDataView::set(ExecState*, JSObject*, unsigned, unsigned)
     81bool JSDataView::set(ExecState*, unsigned, JSObject*, unsigned, unsigned)
    8282{
    8383    UNREACHABLE_FOR_PLATFORM();
  • trunk/Source/JavaScriptCore/runtime/JSDataView.h

    r191215 r197192  
    4949    static JSDataView* createUninitialized(ExecState*, Structure*, unsigned length);
    5050    static JSDataView* create(ExecState*, Structure*, unsigned length);
    51     bool set(ExecState*, JSObject*, unsigned offset, unsigned length);
     51    bool set(ExecState*, unsigned, JSObject*, unsigned, unsigned length);
    5252    bool setIndex(ExecState*, unsigned, JSValue);
    5353   
  • trunk/Source/JavaScriptCore/runtime/JSGenericTypedArrayView.h

    r195701 r197192  
    8484// };
    8585
     86enum class CopyType {
     87    LeftToRight,
     88    Unobservable,
     89};
     90
    8691template<typename Adaptor>
    8792class JSGenericTypedArrayView : public JSArrayBufferView {
     
    218223    // appropriate exception.
    219224    bool validateRange(ExecState*, unsigned offset, unsigned length);
    220    
     225
    221226    // Returns true if successful, and false on error; if it returns false
    222227    // then it will have thrown an exception.
    223     bool set(ExecState*, JSObject*, unsigned offset, unsigned length);
     228    bool set(ExecState*, unsigned offset, JSObject*, unsigned objectOffset, unsigned length, CopyType type = CopyType::Unobservable);
    224229   
    225230    PassRefPtr<typename Adaptor::ViewType> typedImpl()
     
    292297    template<typename OtherAdaptor>
    293298    bool setWithSpecificType(
    294         ExecState*, JSGenericTypedArrayView<OtherAdaptor>*,
    295         unsigned offset, unsigned length);
     299        ExecState*, unsigned offset, JSGenericTypedArrayView<OtherAdaptor>*,
     300        unsigned objectOffset, unsigned length, CopyType);
    296301
    297302    // The ECMA 6 spec states that floating point Typed Arrays should have the following ordering:
  • trunk/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewConstructorInlines.h

    r196986 r197192  
    7878
    7979template<typename ViewClass>
    80 static JSObject* constructGenericTypedArrayViewFromIterator(ExecState* exec, Structure* structure, JSValue iterator)
     80inline JSObject* constructGenericTypedArrayViewFromIterator(ExecState* exec, Structure* structure, JSValue iterator)
    8181{
    8282    if (!iterator.isObject())
     
    116116
    117117template<typename ViewClass>
    118 static JSObject* constructGenericTypedArrayViewWithArguments(ExecState* exec, Structure* structure, EncodedJSValue firstArgument, unsigned offset, Optional<unsigned> lengthOpt)
     118inline JSObject* constructGenericTypedArrayViewWithArguments(ExecState* exec, Structure* structure, EncodedJSValue firstArgument, unsigned offset, Optional<unsigned> lengthOpt)
    119119{
    120120    JSValue firstValue = JSValue::decode(firstArgument);
     
    194194        }
    195195       
    196         if (!result->set(exec, object, 0, length))
     196        if (!result->set(exec, 0, object, 0, length))
    197197            return nullptr;
    198198       
     
    217217
    218218template<typename ViewClass>
    219 static EncodedJSValue JSC_HOST_CALL constructGenericTypedArrayView(ExecState* exec)
     219EncodedJSValue JSC_HOST_CALL constructGenericTypedArrayView(ExecState*);
     220
     221template<typename ViewClass>
     222EncodedJSValue JSC_HOST_CALL constructGenericTypedArrayView(ExecState* exec)
    220223{
    221224    Structure* structure = InternalFunction::createSubclassStructure(exec, exec->newTarget(), asInternalFunction(exec->callee())->globalObject()->typedArrayStructure(ViewClass::TypedArrayStorageType));
  • trunk/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewInlines.h

    r196179 r197192  
    139139template<typename OtherAdaptor>
    140140bool JSGenericTypedArrayView<Adaptor>::setWithSpecificType(
    141     ExecState* exec, JSGenericTypedArrayView<OtherAdaptor>* other,
    142     unsigned offset, unsigned length)
     141    ExecState* exec, unsigned offset, JSGenericTypedArrayView<OtherAdaptor>* other,
     142    unsigned otherOffset, unsigned length, CopyType type)
    143143{
    144144    // Handle the hilarious case: the act of getting the length could have resulted
     
    149149    // but we won't have a security vulnerability.
    150150    length = std::min(length, other->length());
    151    
     151
     152    RELEASE_ASSERT(other->canAccessRangeQuickly(otherOffset, length));
    152153    if (!validateRange(exec, offset, length))
    153154        return false;
    154    
    155     if (other->length() != length) {
    156         exec->vm().throwException(exec, createRangeError(exec, "Length of incoming array changed unexpectedly."));
    157         return false;
    158     }
    159155   
    160156    // This method doesn't support copying between the same array. Note that
     
    186182
    187183    unsigned otherElementSize = sizeof(typename OtherAdaptor::Type);
    188    
    189     // Handle cases (1) and (2B).
     184
     185    // Handle cases (1) and (2A).
    190186    if (!hasArrayBuffer() || !other->hasArrayBuffer()
    191187        || existingBuffer() != other->existingBuffer()
    192         || (elementSize == otherElementSize && vector() > other->vector())) {
     188        || (elementSize == otherElementSize && vector() <= other->vector())
     189        || type == CopyType::LeftToRight) {
     190        for (unsigned i = 0; i < length; ++i) {
     191            setIndexQuicklyToNativeValue(
     192                offset + i, OtherAdaptor::template convertTo<Adaptor>(
     193                    other->getIndexQuicklyAsNativeValue(i + otherOffset)));
     194        }
     195        return true;
     196    }
     197
     198    // Now we either have (2B) or (3) - so first we try to cover (2B).
     199    if (elementSize == otherElementSize) {
    193200        for (unsigned i = length; i--;) {
    194201            setIndexQuicklyToNativeValue(
    195202                offset + i, OtherAdaptor::template convertTo<Adaptor>(
    196                     other->getIndexQuicklyAsNativeValue(i)));
    197         }
    198         return true;
    199     }
    200    
    201     // Now we either have (2A) or (3) - so first we try to cover (2A).
    202     if (elementSize == otherElementSize) {
    203         for (unsigned i = 0; i < length; ++i) {
    204             setIndexQuicklyToNativeValue(
    205                 offset + i, OtherAdaptor::template convertTo<Adaptor>(
    206                     other->getIndexQuicklyAsNativeValue(i)));
     203                    other->getIndexQuicklyAsNativeValue(i + otherOffset)));
    207204        }
    208205        return true;
     
    213210    for (unsigned i = length; i--;) {
    214211        transferBuffer[i] = OtherAdaptor::template convertTo<Adaptor>(
    215             other->getIndexQuicklyAsNativeValue(i));
     212            other->getIndexQuicklyAsNativeValue(i + otherOffset));
    216213    }
    217214    for (unsigned i = length; i--;)
     
    223220template<typename Adaptor>
    224221bool JSGenericTypedArrayView<Adaptor>::set(
    225     ExecState* exec, JSObject* object, unsigned offset, unsigned length)
     222    ExecState* exec, unsigned offset, JSObject* object, unsigned objectOffset, unsigned length, CopyType type)
    226223{
    227224    const ClassInfo* ci = object->classInfo();
     
    231228        length = std::min(length, other->length());
    232229       
     230        RELEASE_ASSERT(other->canAccessRangeQuickly(objectOffset, length));
    233231        if (!validateRange(exec, offset, length))
    234232            return false;
    235        
    236         memmove(typedVector() + offset, other->typedVector(), other->byteLength());
     233
     234        memmove(typedVector() + offset, other->typedVector() + objectOffset, other->byteLength());
    237235        return true;
    238236    }
     
    241239    case TypeInt8:
    242240        return setWithSpecificType<Int8Adaptor>(
    243             exec, jsCast<JSInt8Array*>(object), offset, length);
     241            exec, offset, jsCast<JSInt8Array*>(object), objectOffset, length, type);
    244242    case TypeInt16:
    245243        return setWithSpecificType<Int16Adaptor>(
    246             exec, jsCast<JSInt16Array*>(object), offset, length);
     244            exec, offset, jsCast<JSInt16Array*>(object), objectOffset, length, type);
    247245    case TypeInt32:
    248246        return setWithSpecificType<Int32Adaptor>(
    249             exec, jsCast<JSInt32Array*>(object), offset, length);
     247            exec, offset, jsCast<JSInt32Array*>(object), objectOffset, length, type);
    250248    case TypeUint8:
    251249        return setWithSpecificType<Uint8Adaptor>(
    252             exec, jsCast<JSUint8Array*>(object), offset, length);
     250            exec, offset, jsCast<JSUint8Array*>(object), objectOffset, length, type);
    253251    case TypeUint8Clamped:
    254252        return setWithSpecificType<Uint8ClampedAdaptor>(
    255             exec, jsCast<JSUint8ClampedArray*>(object), offset, length);
     253            exec, offset, jsCast<JSUint8ClampedArray*>(object), objectOffset, length, type);
    256254    case TypeUint16:
    257255        return setWithSpecificType<Uint16Adaptor>(
    258             exec, jsCast<JSUint16Array*>(object), offset, length);
     256            exec, offset, jsCast<JSUint16Array*>(object), objectOffset, length, type);
    259257    case TypeUint32:
    260258        return setWithSpecificType<Uint32Adaptor>(
    261             exec, jsCast<JSUint32Array*>(object), offset, length);
     259            exec, offset, jsCast<JSUint32Array*>(object), objectOffset, length, type);
    262260    case TypeFloat32:
    263261        return setWithSpecificType<Float32Adaptor>(
    264             exec, jsCast<JSFloat32Array*>(object), offset, length);
     262            exec, offset, jsCast<JSFloat32Array*>(object), objectOffset, length, type);
    265263    case TypeFloat64:
    266264        return setWithSpecificType<Float64Adaptor>(
    267             exec, jsCast<JSFloat64Array*>(object), offset, length);
     265            exec, offset, jsCast<JSFloat64Array*>(object), objectOffset, length, type);
    268266    case NotTypedArray:
    269267    case TypeDataView: {
    270268        if (!validateRange(exec, offset, length))
    271269            return false;
     270
    272271        // We could optimize this case. But right now, we don't.
    273272        for (unsigned i = 0; i < length; ++i) {
    274             JSValue value = object->get(exec, i);
     273            JSValue value = object->get(exec, i + objectOffset);
    275274            if (!setIndex(exec, offset + i, value))
    276275                return false;
  • trunk/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewPrototypeFunctions.h

    r195416 r197192  
    4646static const char* typedArrayBufferHasBeenDetachedErrorMessage = "Underlying ArrayBuffer has been detached from the view";
    4747
     48// This implements 22.2.4.7 TypedArraySpeciesCreate
     49// Note, that this function throws.
     50template<typename Functor>
     51inline JSArrayBufferView* speciesConstruct(ExecState* exec, JSObject* exemplar, MarkedArgumentBuffer& args, const Functor& defaultConstructor)
     52{
     53    JSValue constructor = exemplar->get(exec, exec->propertyNames().constructor);
     54    if (exec->hadException())
     55        return nullptr;
     56
     57    if (constructor.isUndefined())
     58        return defaultConstructor();
     59    if (!constructor.isObject()) {
     60        throwTypeError(exec, "constructor Property should not be null");
     61        return nullptr;
     62    }
     63
     64    JSValue species = constructor.get(exec, exec->propertyNames().speciesSymbol);
     65    if (exec->hadException())
     66        return nullptr;
     67
     68    if (species.isUndefinedOrNull())
     69        return defaultConstructor();
     70
     71    JSValue result = construct(exec, species, args, "species is not a constructor");
     72    if (exec->hadException())
     73        return nullptr;
     74
     75    if (JSArrayBufferView* view = jsDynamicCast<JSArrayBufferView*>(result))
     76        return view;
     77
     78    throwTypeError(exec, "species constructor did not return a TypedArray View");
     79    return nullptr;
     80}
     81
    4882inline unsigned argumentClampedIndexFromStartOrEnd(ExecState* exec, int argument, unsigned length, unsigned undefinedValue = 0)
    4983{
     
    100134        return JSValue::encode(jsUndefined());
    101135
    102     thisObject->set(exec, sourceArray, offset, length);
     136    thisObject->set(exec, offset, sourceArray, 0, length, CopyType::Unobservable);
    103137    return JSValue::encode(jsUndefined());
    104138}
     
    364398    unsigned length = end - begin;
    365399
    366     typename ViewClass::ElementType* array = thisObject->typedVector();
    367 
    368     Structure* structure =
    369     callee->globalObject()->typedArrayStructure(ViewClass::TypedArrayStorageType);
    370 
    371     ViewClass* result = ViewClass::createUninitialized(exec, structure, length);
    372 
    373     // We can use memcpy since we know this a new buffer
    374     memcpy(static_cast<void*>(result->typedVector()), static_cast<void*>(array + begin), length * thisObject->elementSize);
     400    MarkedArgumentBuffer args;
     401    args.append(jsNumber(length));
     402
     403    JSArrayBufferView* result = speciesConstruct(exec, thisObject, args, [&]() {
     404        Structure* structure = callee->globalObject()->typedArrayStructure(ViewClass::TypedArrayStorageType);
     405        return ViewClass::createUninitialized(exec, structure, length);
     406    });
     407    if (exec->hadException())
     408        return JSValue::encode(JSValue());
     409
     410    // We return early here since we don't allocate a backing store if length is 0 and memmove does not like nullptrs
     411    if (!length)
     412        return JSValue::encode(result);
     413
     414    // The species constructor may return an array with any arbitrary length.
     415    length = std::min(length, result->length());
     416    switch (result->classInfo()->typedArrayStorageType) {
     417    case TypeInt8:
     418        jsCast<JSInt8Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
     419        break;
     420    case TypeInt16:
     421        jsCast<JSInt16Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
     422        break;
     423    case TypeInt32:
     424        jsCast<JSInt32Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
     425        break;
     426    case TypeUint8:
     427        jsCast<JSUint8Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
     428        break;
     429    case TypeUint8Clamped:
     430        jsCast<JSUint8ClampedArray*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
     431        break;
     432    case TypeUint16:
     433        jsCast<JSUint16Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
     434        break;
     435    case TypeUint32:
     436        jsCast<JSUint32Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
     437        break;
     438    case TypeFloat32:
     439        jsCast<JSFloat32Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
     440        break;
     441    case TypeFloat64:
     442        jsCast<JSFloat64Array*>(result)->set(exec, 0, thisObject, begin, length, CopyType::LeftToRight);
     443        break;
     444    default:
     445        RELEASE_ASSERT_NOT_REACHED();
     446    }
    375447
    376448    return JSValue::encode(result);
     
    406478    RELEASE_ASSERT(thisLength == thisObject->length());
    407479
    408     Structure* structure =
    409     callee->globalObject()->typedArrayStructure(ViewClass::TypedArrayStorageType);
    410 
    411     ViewClass* result = ViewClass::create(
    412         exec, structure, arrayBuffer,
    413         thisObject->byteOffset() + offset * ViewClass::elementSize,
    414         length);
     480    unsigned newByteOffset = thisObject->byteOffset() + offset * ViewClass::elementSize;
     481
     482    MarkedArgumentBuffer args;
     483    args.append(exec->vm().m_typedArrayController->toJS(exec, thisObject->globalObject(), thisObject->buffer()));
     484    args.append(jsNumber(newByteOffset));
     485    args.append(jsNumber(length));
     486
     487    JSArrayBufferView* result = speciesConstruct(exec, thisObject, args, [&]() {
     488        Structure* structure = callee->globalObject()->typedArrayStructure(ViewClass::TypedArrayStorageType);
     489
     490        return ViewClass::create(
     491            exec, structure, arrayBuffer,
     492            thisObject->byteOffset() + offset * ViewClass::elementSize,
     493            length);
     494    });
     495    if (exec->hadException())
     496        return JSValue::encode(JSValue());
    415497
    416498    return JSValue::encode(result);
Note: See TracChangeset for help on using the changeset viewer.