Changeset 191190 in webkit


Ignore:
Timestamp:
Oct 16, 2015 11:37:14 AM (9 years ago)
Author:
keith_miller@apple.com
Message:

Fix some issues with TypedArrays
https://bugs.webkit.org/show_bug.cgi?id=150216

Reviewed by Michael Saboff.

This fixes a couple of issues:
1) The DFG had a separate case for creating new typedarrays in the dfg when the first argument is an object.

Since the code for creating a Typedarray in the dfg is almost the same as the code in Baseline/LLInt
the two cases have been merged.

2) If the length property on an object was unset then the construction could crash.
3) The TypedArray.prototype.set function and the TypedArray constructor should not call Get? for the

length of the source object when the source object is a TypedArray.

4) The conditions that were used to decide if the iterator could be skipped were incorrect.

Instead of checking for have a bad time we should have checked the Indexing type did not allow for
indexed accessors.

  • dfg/DFGOperations.cpp:

(JSC::DFG::newTypedArrayWithOneArgument): Deleted.

  • runtime/JSGenericTypedArrayViewConstructorInlines.h:

(JSC::constructGenericTypedArrayViewFromIterator):
(JSC::constructGenericTypedArrayViewWithFirstArgument):
(JSC::constructGenericTypedArrayView):

  • runtime/JSGenericTypedArrayViewPrototypeFunctions.h:

(JSC::genericTypedArrayViewProtoFuncSet):

  • tests/stress/typedarray-construct-iterator.js: Added.

(iterator.return.next):
(iterator):
(body):

Location:
trunk/Source/JavaScriptCore
Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r191175 r191190  
     12015-10-16  Keith Miller  <keith_miller@apple.com>
     2
     3        Fix some issues with TypedArrays
     4        https://bugs.webkit.org/show_bug.cgi?id=150216
     5
     6        Reviewed by Michael Saboff.
     7
     8        This fixes a couple of issues:
     9        1) The DFG had a separate case for creating new typedarrays in the dfg when the first argument is an object.
     10           Since the code for creating a Typedarray in the dfg is almost the same as the code in Baseline/LLInt
     11           the two cases have been merged.
     12        2) If the length property on an object was unset then the construction could crash.
     13        3) The TypedArray.prototype.set function and the TypedArray constructor should not call [[Get]] for the
     14           length of the source object when the source object is a TypedArray.
     15        4) The conditions that were used to decide if the iterator could be skipped were incorrect.
     16           Instead of checking for have a bad time we should have checked the Indexing type did not allow for
     17           indexed accessors.
     18
     19        * dfg/DFGOperations.cpp:
     20        (JSC::DFG::newTypedArrayWithOneArgument): Deleted.
     21        * runtime/JSGenericTypedArrayViewConstructorInlines.h:
     22        (JSC::constructGenericTypedArrayViewFromIterator):
     23        (JSC::constructGenericTypedArrayViewWithFirstArgument):
     24        (JSC::constructGenericTypedArrayView):
     25        * runtime/JSGenericTypedArrayViewPrototypeFunctions.h:
     26        (JSC::genericTypedArrayViewProtoFuncSet):
     27        * tests/stress/typedarray-construct-iterator.js: Added.
     28        (iterator.return.next):
     29        (iterator):
     30        (body):
     31
    1322015-10-15  Michael Saboff  <msaboff@apple.com>
    233
  • trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp

    r190896 r191190  
    143143}
    144144
    145 template<typename ViewClass>
    146 char* newTypedArrayWithOneArgument(
    147     ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
    148 {
    149     VM& vm = exec->vm();
    150     NativeCallFrameTracer tracer(&vm, exec);
    151    
    152     JSValue value = JSValue::decode(encodedValue);
    153    
    154     if (JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(value)) {
    155         RefPtr<ArrayBuffer> buffer = jsBuffer->impl();
    156        
    157         if (buffer->byteLength() % ViewClass::elementSize) {
    158             vm.throwException(exec, createRangeError(exec, ASCIILiteral("ArrayBuffer length minus the byteOffset is not a multiple of the element size")));
    159             return 0;
    160         }
    161         return bitwise_cast<char*>(
    162             ViewClass::create(
    163                 exec, structure, buffer, 0, buffer->byteLength() / ViewClass::elementSize));
    164     }
    165    
    166     if (JSObject* object = jsDynamicCast<JSObject*>(value)) {
    167         unsigned length = object->get(exec, vm.propertyNames->length).toUInt32(exec);
    168         if (exec->hadException())
    169             return 0;
    170        
    171         ViewClass* result = ViewClass::createUninitialized(exec, structure, length);
    172         if (!result)
    173             return 0;
    174        
    175         if (!result->set(exec, object, 0, length))
    176             return 0;
    177        
    178         return bitwise_cast<char*>(result);
    179     }
    180    
    181     int length;
    182     if (value.isInt32())
    183         length = value.asInt32();
    184     else if (!value.isNumber()) {
    185         vm.throwException(exec, createTypeError(exec, ASCIILiteral("Invalid array length argument")));
    186         return 0;
    187     } else {
    188         length = static_cast<int>(value.asNumber());
    189         if (length != value.asNumber()) {
    190             vm.throwException(exec, createTypeError(exec, ASCIILiteral("Invalid array length argument (fractional lengths not allowed)")));
    191             return 0;
    192         }
    193     }
    194    
    195     if (length < 0) {
    196         vm.throwException(exec, createRangeError(exec, ASCIILiteral("Requested length is negative")));
    197         return 0;
    198     }
    199    
    200     return bitwise_cast<char*>(ViewClass::create(exec, structure, length));
    201 }
    202 
    203145extern "C" {
    204146
     
    665607    ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
    666608{
    667     return newTypedArrayWithOneArgument<JSInt8Array>(exec, structure, encodedValue);
     609    return bitwise_cast<char*>(constructGenericTypedArrayViewWithFirstArgument<JSInt8Array>(exec, structure, encodedValue));
    668610}
    669611
     
    677619    ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
    678620{
    679     return newTypedArrayWithOneArgument<JSInt16Array>(exec, structure, encodedValue);
     621    return bitwise_cast<char*>(constructGenericTypedArrayViewWithFirstArgument<JSInt16Array>(exec, structure, encodedValue));
    680622}
    681623
     
    689631    ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
    690632{
    691     return newTypedArrayWithOneArgument<JSInt32Array>(exec, structure, encodedValue);
     633    return bitwise_cast<char*>(constructGenericTypedArrayViewWithFirstArgument<JSInt32Array>(exec, structure, encodedValue));
    692634}
    693635
     
    701643    ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
    702644{
    703     return newTypedArrayWithOneArgument<JSUint8Array>(exec, structure, encodedValue);
     645    return bitwise_cast<char*>(constructGenericTypedArrayViewWithFirstArgument<JSUint8Array>(exec, structure, encodedValue));
    704646}
    705647
     
    713655    ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
    714656{
    715     return newTypedArrayWithOneArgument<JSUint8ClampedArray>(exec, structure, encodedValue);
     657    return bitwise_cast<char*>(constructGenericTypedArrayViewWithFirstArgument<JSUint8ClampedArray>(exec, structure, encodedValue));
    716658}
    717659
     
    725667    ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
    726668{
    727     return newTypedArrayWithOneArgument<JSUint16Array>(exec, structure, encodedValue);
     669    return bitwise_cast<char*>(constructGenericTypedArrayViewWithFirstArgument<JSUint16Array>(exec, structure, encodedValue));
    728670}
    729671
     
    737679    ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
    738680{
    739     return newTypedArrayWithOneArgument<JSUint32Array>(exec, structure, encodedValue);
     681    return bitwise_cast<char*>(constructGenericTypedArrayViewWithFirstArgument<JSUint32Array>(exec, structure, encodedValue));
    740682}
    741683
     
    749691    ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
    750692{
    751     return newTypedArrayWithOneArgument<JSFloat32Array>(exec, structure, encodedValue);
     693    return bitwise_cast<char*>(constructGenericTypedArrayViewWithFirstArgument<JSFloat32Array>(exec, structure, encodedValue));
    752694}
    753695
     
    761703    ExecState* exec, Structure* structure, EncodedJSValue encodedValue)
    762704{
    763     return newTypedArrayWithOneArgument<JSFloat64Array>(exec, structure, encodedValue);
     705    return bitwise_cast<char*>(constructGenericTypedArrayViewWithFirstArgument<JSFloat64Array>(exec, structure, encodedValue));
    764706}
    765707
  • trunk/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewConstructorInlines.h

    r191059 r191190  
    7878
    7979template<typename ViewClass>
    80 static EncodedJSValue constructGenericTypedArrayViewFromIterator(ExecState* exec, Structure* structure, JSValue iterator)
     80static JSObject* constructGenericTypedArrayViewFromIterator(ExecState* exec, Structure* structure, JSValue iterator)
    8181{
    8282    if (!iterator.isObject())
    83         return JSValue::encode(throwTypeError(exec, "Symbol.Iterator for the first argument did not return an object."));
     83        return throwTypeError(exec, "Symbol.Iterator for the first argument did not return an object.");
    8484
    8585    MarkedArgumentBuffer storage;
     
    8787        JSValue next = iteratorStep(exec, iterator);
    8888        if (exec->hadException())
    89             return JSValue::encode(jsUndefined());
     89            return nullptr;
    9090
    9191        if (next.isFalse())
     
    9494        JSValue nextItem = iteratorValue(exec, next);
    9595        if (exec->hadException())
    96             return JSValue::encode(jsUndefined());
     96            return nullptr;
    9797
    9898        storage.append(nextItem);
     
    102102    if (!result) {
    103103        ASSERT(exec->hadException());
    104         return JSValue::encode(jsUndefined());
     104        return nullptr;
    105105    }
    106106
     
    108108        if (!result->setIndex(exec, i, storage.at(i))) {
    109109            ASSERT(exec->hadException());
    110             return JSValue::encode(jsUndefined());
    111         }
    112     }
    113 
    114     return JSValue::encode(result);
    115 }
    116 
    117 template<typename ViewClass>
    118 static EncodedJSValue JSC_HOST_CALL constructGenericTypedArrayView(ExecState* exec)
    119 {
    120     Structure* structure =
    121         asInternalFunction(exec->callee())->globalObject()->typedArrayStructure(
    122             ViewClass::TypedArrayStorageType);
    123 
     110            return nullptr;
     111        }
     112    }
     113
     114    return result;
     115}
     116
     117template<typename ViewClass, bool checkForOtherArguments = false>
     118static JSObject* constructGenericTypedArrayViewWithFirstArgument(ExecState* exec, Structure* structure, EncodedJSValue firstArgument)
     119{
     120    JSValue firstValue = JSValue::decode(firstArgument);
    124121    VM& vm = exec->vm();
    125122
    126     if (!exec->argumentCount()) {
    127         if (ViewClass::TypedArrayStorageType == TypeDataView)
    128             return throwVMError(exec, createTypeError(exec, "DataView constructor requires at least one argument."));
    129        
    130         // Even though the documentation doesn't say so, it's correct to say
    131         // "new Int8Array()". This is the same as allocating an array of zero
    132         // length.
    133         return JSValue::encode(ViewClass::create(exec, structure, 0));
    134     }
    135    
    136     if (JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(exec->argument(0))) {
     123    if (JSArrayBuffer* jsBuffer = jsDynamicCast<JSArrayBuffer*>(firstValue)) {
    137124        RefPtr<ArrayBuffer> buffer = jsBuffer->impl();
    138        
    139         unsigned offset = (exec->argumentCount() > 1) ? exec->uncheckedArgument(1).toUInt32(exec) : 0;
    140         if (exec->hadException())
    141             return JSValue::encode(jsUndefined());
     125
     126        unsigned offset = 0;
    142127        unsigned length = 0;
    143         if (exec->argumentCount() > 2) {
     128        if (checkForOtherArguments && exec->argumentCount() > 1) {
     129            offset = exec->uncheckedArgument(1).toUInt32(exec);
     130            if (exec->hadException())
     131                return nullptr;
     132        }
     133
     134        if (checkForOtherArguments && exec->argumentCount() > 2) {
    144135            length = exec->uncheckedArgument(2).toUInt32(exec);
    145136            if (exec->hadException())
    146                 return JSValue::encode(jsUndefined());
     137                return nullptr;
    147138        } else {
    148139            if ((buffer->byteLength() - offset) % ViewClass::elementSize)
    149                 return throwVMError(exec, createRangeError(exec, "ArrayBuffer length minus the byteOffset is not a multiple of the element size"));
     140                return throwRangeError(exec, "ArrayBuffer length minus the byteOffset is not a multiple of the element size");
    150141            length = (buffer->byteLength() - offset) / ViewClass::elementSize;
    151         }
    152         return JSValue::encode(ViewClass::create(exec, structure, buffer, offset, length));
     142
     143        }
     144
     145        return ViewClass::create(exec, structure, buffer, offset, length);
    153146    }
    154147   
    155148    if (ViewClass::TypedArrayStorageType == TypeDataView)
    156         return throwVMError(exec, createTypeError(exec, "Expected ArrayBuffer for the first argument."));
     149        return throwTypeError(exec, "Expected ArrayBuffer for the first argument.");
    157150   
    158151    // For everything but DataView, we allow construction with any of:
    159152    // - Another array. This creates a copy of the of that array.
    160153    // - An integer. This creates a new typed array of that length and zero-initializes it.
    161    
    162     if (JSObject* object = jsDynamicCast<JSObject*>(exec->uncheckedArgument(0))) {
    163         PropertySlot lengthSlot(object);
    164         object->getPropertySlot(exec, vm.propertyNames->length, lengthSlot);
    165 
    166         if (!isTypedView(object->classInfo()->typedArrayStorageType)) {
     154
     155    if (JSObject* object = jsDynamicCast<JSObject*>(firstValue)) {
     156        unsigned length;
     157
     158        if (isTypedView(object->classInfo()->typedArrayStorageType))
     159            length = jsCast<JSArrayBufferView*>(object)->length();
     160        else {
     161            PropertySlot lengthSlot(object);
     162            object->getPropertySlot(exec, vm.propertyNames->length, lengthSlot);
     163
    167164            JSValue iteratorFunc = object->get(exec, vm.propertyNames->iteratorSymbol);
    168165            if (exec->hadException())
    169                 return JSValue::encode(jsUndefined());
     166                return nullptr;
    170167
    171168            // We would like not use the iterator as it is painfully slow. Fortunately, unless
    172169            // 1) The iterator is not a known iterator.
    173170            // 2) The base object does not have a length getter.
    174             // 3) Bad times are being had.
     171            // 3) The base object might have indexed getters.
    175172            // it should not be observable that we do not use the iterator.
    176173
    177174            if (!iteratorFunc.isUndefined()
    178                 && (iteratorFunc != exec->lexicalGlobalObject()->arrayProtoValuesFunction()
     175                && (iteratorFunc != object->globalObject()->arrayProtoValuesFunction()
    179176                    || lengthSlot.isAccessor() || lengthSlot.isCustom()
    180                     || exec->lexicalGlobalObject()->isHavingABadTime())) {
     177                    || hasAnyArrayStorage(object->indexingType()))) {
    181178
    182179                    CallData callData;
    183180                    CallType callType = getCallData(iteratorFunc, callData);
    184181                    if (callType == CallTypeNone)
    185                         return JSValue::encode(throwTypeError(exec, "Symbol.Iterator for the first argument cannot be called."));
     182                        return throwTypeError(exec, "Symbol.Iterator for the first argument cannot be called.");
    186183
    187184                    ArgList arguments;
    188185                    JSValue iterator = call(exec, iteratorFunc, callType, callData, object, arguments);
    189186                    if (exec->hadException())
    190                         return JSValue::encode(jsUndefined());
     187                        return nullptr;
    191188
    192189                    return constructGenericTypedArrayViewFromIterator<ViewClass>(exec, structure, iterator);
    193 
    194190            }
    195         }
    196 
    197         unsigned length = lengthSlot.getValue(exec, vm.propertyNames->length).toUInt32(exec);
    198         if (exec->hadException())
    199             return JSValue::encode(jsUndefined());
     191
     192            length = lengthSlot.isUnset() ? 0 : lengthSlot.getValue(exec, vm.propertyNames->length).toUInt32(exec);
     193            if (exec->hadException())
     194                return nullptr;
     195        }
     196
    200197       
    201198        ViewClass* result = ViewClass::createUninitialized(exec, structure, length);
    202199        if (!result) {
    203200            ASSERT(exec->hadException());
    204             return JSValue::encode(jsUndefined());
     201            return nullptr;
    205202        }
    206203       
    207204        if (!result->set(exec, object, 0, length))
    208             return JSValue::encode(jsUndefined());
     205            return nullptr;
    209206       
    210         return JSValue::encode(result);
     207        return result;
    211208    }
    212209   
    213210    int length;
    214     if (exec->uncheckedArgument(0).isInt32())
    215         length = exec->uncheckedArgument(0).asInt32();
    216     else if (!exec->uncheckedArgument(0).isNumber())
    217         return throwVMError(exec, createTypeError(exec, "Invalid array length argument"));
     211    if (firstValue.isInt32())
     212        length = firstValue.asInt32();
     213    else if (!firstValue.isNumber())
     214        return throwTypeError(exec, "Invalid array length argument");
    218215    else {
    219         length = static_cast<int>(exec->uncheckedArgument(0).asNumber());
    220         if (length != exec->uncheckedArgument(0).asNumber())
    221             return throwVMError(exec, createTypeError(exec, "Invalid array length argument (fractional lengths not allowed)"));
     216        length = static_cast<int>(firstValue.asNumber());
     217        if (length != firstValue.asNumber())
     218            return throwTypeError(exec, "Invalid array length argument (fractional lengths not allowed)");
    222219    }
    223220
    224221    if (length < 0)
    225         return throwVMError(exec, createRangeError(exec, "Requested length is negative"));
    226     return JSValue::encode(ViewClass::create(exec, structure, length));
     222        return throwRangeError(exec, "Requested length is negative");
     223    return ViewClass::create(exec, structure, length);
     224}
     225
     226template<typename ViewClass>
     227static EncodedJSValue JSC_HOST_CALL constructGenericTypedArrayView(ExecState* exec)
     228{
     229    Structure* structure =
     230        asInternalFunction(exec->callee())->globalObject()->typedArrayStructure(
     231            ViewClass::TypedArrayStorageType);
     232
     233    if (!exec->argumentCount()) {
     234        if (ViewClass::TypedArrayStorageType == TypeDataView)
     235            return throwVMError(exec, createTypeError(exec, "DataView constructor requires at least one argument."));
     236
     237        return JSValue::encode(ViewClass::create(exec, structure, 0));
     238    }
     239
     240    return JSValue::encode(constructGenericTypedArrayViewWithFirstArgument<ViewClass, true>(exec, structure, JSValue::encode(exec->uncheckedArgument(0))));
    227241}
    228242
  • trunk/Source/JavaScriptCore/runtime/JSGenericTypedArrayViewPrototypeFunctions.h

    r191059 r191190  
    6767        return throwVMError(exec, createTypeError(exec, "Expected at least one argument"));
    6868
    69     JSObject* sourceArray = jsDynamicCast<JSObject*>(exec->uncheckedArgument(0));
    70     if (!sourceArray)
    71         return throwVMError(exec, createTypeError(exec, "First argument should be an object"));
    72 
    7369    unsigned offset;
    7470    if (exec->argumentCount() >= 2) {
     
    7975        offset = 0;
    8076
    81     unsigned length = sourceArray->get(exec, exec->vm().propertyNames->length).toUInt32(exec);
     77    JSObject* sourceArray = jsDynamicCast<JSObject*>(exec->uncheckedArgument(0));
     78    if (!sourceArray)
     79        return throwVMError(exec, createTypeError(exec, "First argument should be an object"));
     80
     81    unsigned length;
     82    if (isTypedView(sourceArray->classInfo()->typedArrayStorageType))
     83        length = jsDynamicCast<JSArrayBufferView*>(sourceArray)->length();
     84    else
     85        length = sourceArray->get(exec, exec->vm().propertyNames->length).toUInt32(exec);
     86
    8287    if (exec->hadException())
    8388        return JSValue::encode(jsUndefined());
     
    284289}
    285290
    286 
    287291template<typename ViewClass>
    288292EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncReverse(ExecState* exec)
Note: See TracChangeset for help on using the changeset viewer.