Changeset 215777 in webkit
- Timestamp:
- Apr 25, 2017 5:52:35 PM (7 years ago)
- Location:
- trunk
- Files:
-
- 5 added
- 19 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JSTests/ChangeLog
r215770 r215777 1 2017-04-25 Saam Barati <sbarati@apple.com> 2 3 JSArray::isArrayPrototypeIteratorProtocolFastAndNonObservable is wrong because it does not do the necessary checks on the base object 4 https://bugs.webkit.org/show_bug.cgi?id=171150 5 <rdar://problem/31771880> 6 7 Reviewed by Sam Weinig. 8 9 * stress/spread-optimized-properly.js: Added. 10 (assert): 11 (test): 12 (shallowEq): 13 (makeArrayIterator): 14 (test.bar): 15 (test.callback): 16 (test.arr.__proto__.Symbol.iterator): 17 (test.arr.Symbol.iterator): 18 (test.get bar): 19 (test.hackedNext): 20 (test.test): 21 (test.): 22 1 23 2017-04-25 Mark Lam <mark.lam@apple.com> 2 24 -
trunk/LayoutTests/ChangeLog
r215776 r215777 1 2017-04-25 Saam Barati <sbarati@apple.com> 2 3 JSArray::isArrayPrototypeIteratorProtocolFastAndNonObservable is wrong because it does not do the necessary checks on the base object 4 https://bugs.webkit.org/show_bug.cgi?id=171150 5 <rdar://problem/31771880> 6 7 Reviewed by Sam Weinig. 8 9 * js/sequence-iterator-protocol-2-expected.txt: Added. 10 * js/sequence-iterator-protocol-2.html: Added. 11 * js/sequence-iterator-protocol-expected.txt: Added. 12 * js/sequence-iterator-protocol.html: Added. 13 1 14 2017-04-25 Ryan Haddad <ryanhaddad@apple.com> 2 15 -
trunk/Source/JavaScriptCore/ChangeLog
r215768 r215777 1 2017-04-25 Saam Barati <sbarati@apple.com> 2 3 JSArray::isArrayPrototypeIteratorProtocolFastAndNonObservable is wrong because it does not do the necessary checks on the base object 4 https://bugs.webkit.org/show_bug.cgi?id=171150 5 <rdar://problem/31771880> 6 7 Reviewed by Sam Weinig. 8 9 This patch fixes a huge oversight from the patch that introduced 10 op_spread/Spread. The original patch did not account for the 11 base object having Symbol.iterator or getters that could 12 change the iterator protocol. This patch fixes the oversight both 13 in the C code, as well as the DFG/FTL backends. We only perform 14 the memcpy version of spread if we've proven that it's guaranteed 15 to be side-effect free (no indexed getters), and if the iterator 16 protocol is guaranteed to be the original protocol. To do this, we 17 must prove that: 18 1. The protocol on Array.prototype hasn't changed (this is the same as the 19 introductory patch for op_spread). 20 2. The base object's __proto__ is Array.prototype 21 3. The base object does not have indexed getters 22 4. The base object does not have Symbol.iterator property. 23 24 * dfg/DFGGraph.cpp: 25 (JSC::DFG::Graph::canDoFastSpread): 26 * dfg/DFGGraph.h: 27 * dfg/DFGSpeculativeJIT.cpp: 28 (JSC::DFG::SpeculativeJIT::compileSpread): 29 * ftl/FTLLowerDFGToB3.cpp: 30 (JSC::FTL::DFG::LowerDFGToB3::compileSpread): 31 * runtime/JSArray.cpp: 32 (JSC::JSArray::isIteratorProtocolFastAndNonObservable): 33 * runtime/JSArray.h: 34 * runtime/JSArrayInlines.h: 35 (JSC::JSArray::isIteratorProtocolFastAndNonObservable): Deleted. 36 * runtime/JSGlobalObject.h: 37 * runtime/JSGlobalObjectInlines.h: 38 (JSC::JSGlobalObject::isArrayPrototypeIteratorProtocolFastAndNonObservable): 39 (JSC::JSGlobalObject::isArrayIteratorProtocolFastAndNonObservable): Deleted. 40 1 41 2017-04-25 Mark Lam <mark.lam@apple.com> 2 42 -
trunk/Source/JavaScriptCore/dfg/DFGGraph.cpp
r211247 r215777 1724 1724 } 1725 1725 1726 bool Graph::canDoFastSpread(Node* node, const AbstractValue& value) 1727 { 1728 // The parameter 'value' is the AbstractValue for child1 (the thing being spread). 1729 ASSERT(node->op() == Spread); 1730 1731 if (node->child1().useKind() != ArrayUse) { 1732 // Note: we only speculate on ArrayUse when we've set up the necessary watchpoints 1733 // to prove that the iteration protocol is non-observable starting from ArrayPrototype. 1734 return false; 1735 } 1736 1737 // FIXME: We should add profiling of the incoming operand to Spread 1738 // so we can speculate in such a way that we guarantee that this 1739 // function would return true: 1740 // https://bugs.webkit.org/show_bug.cgi?id=171198 1741 1742 if (!value.m_structure.isFinite()) 1743 return false; 1744 1745 ArrayPrototype* arrayPrototype = globalObjectFor(node->child1()->origin.semantic)->arrayPrototype(); 1746 bool allGood = true; 1747 value.m_structure.forEach([&] (RegisteredStructure structure) { 1748 allGood &= structure->storedPrototype() == arrayPrototype 1749 && !structure->isDictionary() 1750 && structure->getConcurrently(m_vm.propertyNames->iteratorSymbol.impl()) == invalidOffset 1751 && !structure->mayInterceptIndexedAccesses(); 1752 }); 1753 1754 return allGood; 1755 } 1756 1726 1757 } } // namespace JSC::DFG 1727 1758 -
trunk/Source/JavaScriptCore/dfg/DFGGraph.h
r214323 r215777 885 885 JSArrayBufferView* tryGetFoldableView(JSValue); 886 886 JSArrayBufferView* tryGetFoldableView(JSValue, ArrayMode arrayMode); 887 888 bool canDoFastSpread(Node*, const AbstractValue&); 887 889 888 890 void registerFrozenValues(); -
trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
r215600 r215777 7034 7034 GPRReg argument = operand.gpr(); 7035 7035 7036 if (node->child1().useKind() == ArrayUse) { 7037 // Note: we only speculate on ArrayUse when we've set up the necessary watchpoints 7038 // to prove that the iteration protocol is non-observable. 7036 if (node->child1().useKind() == ArrayUse) 7039 7037 speculateArray(node->child1(), argument); 7040 7038 7039 if (m_jit.graph().canDoFastSpread(node, m_state.forNode(node->child1()))) { 7041 7040 #if USE(JSVALUE64) 7042 7041 GPRTemporary result(this); -
trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
r215720 r215777 4677 4677 4678 4678 LValue result; 4679 if (m_node->child1().useKind() == ArrayUse) { 4679 4680 if (m_node->child1().useKind() == ArrayUse) 4680 4681 speculateArray(m_node->child1()); 4681 4682 4683 if (m_graph.canDoFastSpread(m_node, m_state.forNode(m_node->child1()))) { 4682 4684 LBasicBlock preLoop = m_out.newBlock(); 4683 4685 LBasicBlock loopSelection = m_out.newBlock(); -
trunk/Source/JavaScriptCore/runtime/JSArray.cpp
r215451 r215777 1404 1404 } 1405 1405 1406 bool JSArray::isIteratorProtocolFastAndNonObservable() 1407 { 1408 JSGlobalObject* globalObject = this->globalObject(); 1409 if (!globalObject->isArrayPrototypeIteratorProtocolFastAndNonObservable()) 1410 return false; 1411 1412 Structure* structure = this->structure(); 1413 // This is the fast case. Many arrays will be an original array. 1414 if (globalObject->isOriginalArrayStructure(structure)) 1415 return true; 1416 1417 if (structure->mayInterceptIndexedAccesses()) 1418 return false; 1419 1420 if (structure->storedPrototype() != globalObject->arrayPrototype()) 1421 return false; 1422 1423 if (getDirectOffset(globalObject->vm(), globalObject->vm().propertyNames->iteratorSymbol) != invalidOffset) 1424 return false; 1425 1426 return true; 1427 } 1428 1406 1429 } // namespace JSC -
trunk/Source/JavaScriptCore/runtime/JSArray.h
r215345 r215777 151 151 JS_EXPORT_PRIVATE void copyToArguments(ExecState*, VirtualRegister firstElementDest, unsigned offset, unsigned length); 152 152 153 bool isIteratorProtocolFastAndNonObservable();153 JS_EXPORT_PRIVATE bool isIteratorProtocolFastAndNonObservable(); 154 154 155 155 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, IndexingType indexingType) -
trunk/Source/JavaScriptCore/runtime/JSArrayInlines.h
r211070 r215777 94 94 } 95 95 96 ALWAYS_INLINE bool JSArray::isIteratorProtocolFastAndNonObservable()97 {98 return globalObject()->isArrayIteratorProtocolFastAndNonObservable();99 }100 101 96 } // namespace JSC -
trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h
r215673 r215777 413 413 std::unique_ptr<ArrayIteratorAdaptiveWatchpoint> m_arrayIteratorPrototypeNext; 414 414 415 bool isArray IteratorProtocolFastAndNonObservable();415 bool isArrayPrototypeIteratorProtocolFastAndNonObservable(); 416 416 417 417 TemplateRegistry m_templateRegistry; -
trunk/Source/JavaScriptCore/runtime/JSGlobalObjectInlines.h
r208637 r215777 54 54 55 55 56 ALWAYS_INLINE bool JSGlobalObject::isArray IteratorProtocolFastAndNonObservable()56 ALWAYS_INLINE bool JSGlobalObject::isArrayPrototypeIteratorProtocolFastAndNonObservable() 57 57 { 58 58 // We're fast if we don't have any indexed properties on the prototype. -
trunk/Source/WebCore/ChangeLog
r215775 r215777 1 2017-04-25 Saam Barati <sbarati@apple.com> 2 3 JSArray::isArrayPrototypeIteratorProtocolFastAndNonObservable is wrong because it does not do the necessary checks on the base object 4 https://bugs.webkit.org/show_bug.cgi?id=171150 5 <rdar://problem/31771880> 6 7 Reviewed by Sam Weinig. 8 9 This patch moves the sequence converters to use the now fixed 10 JSArray::isArrayPrototypeIteratorProtocolFastAndNonObservable test 11 inside JSC. 12 13 This patch also fixes a few bugs: 14 1. Converting to a sequence of numbers must prove that the JSArray 15 is filled only with Int32/Double. If there is a chance the array 16 contains objects, the conversion to a numeric IDLType can be observable 17 (via valueOf()), and can change the iterator protocol. 18 2. There are other conversions that can have side effects a-la valueOf(). 19 This patch introduces a new static constant in the various Converter 20 classes that tell the sequence converter if the conversion operation 21 can have JS side effects. If it does have side effects, we fall back to 22 the generic conversion that uses the iterator protocol. If not, we can 23 do a faster version that iterates over each element of the array, 24 reading it directly, and converting it. 25 26 Tests: js/sequence-iterator-protocol-2.html 27 js/sequence-iterator-protocol.html 28 29 * bindings/js/JSDOMConvertAny.h: Does not have side effects. 30 * bindings/js/JSDOMConvertBase.h: We pessimistically assume inside DefaultConverter that converions have side effects. 31 * bindings/js/JSDOMConvertBoolean.h: Does not have side effects. 32 * bindings/js/JSDOMConvertCallbacks.h: Does not have side effects. 33 * bindings/js/JSDOMConvertObject.h: Does not have side effects. 34 * bindings/js/JSDOMConvertSequences.h: 35 (WebCore::Detail::NumericSequenceConverter::convert): 36 (WebCore::Detail::SequenceConverter::convert): 37 1 38 2017-04-25 Michael Saboff <msaboff@apple.com> 2 39 -
trunk/Source/WebCore/bindings/js/JSDOMConvertAny.h
r211821 r215777 33 33 template<> struct Converter<IDLAny> : DefaultConverter<IDLAny> { 34 34 using ReturnType = JSC::JSValue; 35 35 36 static constexpr bool conversionHasSideEffects = false; 37 36 38 static JSC::JSValue convert(JSC::ExecState&, JSC::JSValue value) 37 39 { -
trunk/Source/WebCore/bindings/js/JSDOMConvertBase.h
r211821 r215777 179 179 template<typename T> struct DefaultConverter { 180 180 using ReturnType = typename T::ImplementationType; 181 182 // We assume the worst, subtypes can override to be less pessimistic. 183 // An example of something that can have side effects 184 // is having a converter that does JSC::JSValue::toNumber. 185 // toNumber() in JavaScript can call arbitrary JS functions. 186 // 187 // An example of something that does not have side effects 188 // is something having a converter that does JSC::JSValue::toBoolean. 189 // toBoolean() in JS can't call arbitrary functions. 190 static constexpr bool conversionHasSideEffects = true; 181 191 }; 182 192 -
trunk/Source/WebCore/bindings/js/JSDOMConvertBoolean.h
r211821 r215777 32 32 33 33 template<> struct Converter<IDLBoolean> : DefaultConverter<IDLBoolean> { 34 35 static constexpr bool conversionHasSideEffects = false; 36 34 37 static bool convert(JSC::ExecState& state, JSC::JSValue value) 35 38 { -
trunk/Source/WebCore/bindings/js/JSDOMConvertCallbacks.h
r211821 r215777 32 32 33 33 template<typename T> struct Converter<IDLCallbackFunction<T>> : DefaultConverter<IDLCallbackFunction<T>> { 34 35 static constexpr bool conversionHasSideEffects = false; 36 34 37 template<typename ExceptionThrower = DefaultExceptionThrower> 35 38 static RefPtr<T> convert(JSC::ExecState& state, JSC::JSValue value, JSDOMGlobalObject& globalObject, ExceptionThrower&& exceptionThrower = ExceptionThrower()) -
trunk/Source/WebCore/bindings/js/JSDOMConvertObject.h
r211821 r215777 32 32 33 33 template<> struct Converter<IDLObject> : DefaultConverter<IDLObject> { 34 35 static constexpr bool conversionHasSideEffects = false; 36 34 37 template<typename ExceptionThrower = DefaultExceptionThrower> 35 38 static JSC::Strong<JSC::JSObject> convert(JSC::ExecState& state, JSC::JSValue value, ExceptionThrower&& exceptionThrower = ExceptionThrower()) -
trunk/Source/WebCore/bindings/js/JSDOMConvertSequences.h
r211821 r215777 42 42 static ReturnType convert(JSC::ExecState& state, JSC::JSObject* jsObject) 43 43 { 44 ReturnType result; 44 return convert(state, jsObject, ReturnType()); 45 } 46 47 static ReturnType convert(JSC::ExecState& state, JSC::JSObject* jsObject, ReturnType&& result) 48 { 45 49 forEachInIterable(&state, jsObject, [&result](JSC::VM& vm, JSC::ExecState* state, JSC::JSValue jsValue) { 46 50 auto scope = DECLARE_THROW_SCOPE(vm); … … 79 83 80 84 JSC::JSArray* array = JSC::asArray(object); 81 if (!array-> globalObject()->isArrayIteratorProtocolFastAndNonObservable())85 if (!array->isIteratorProtocolFastAndNonObservable()) 82 86 return GenericConverter::convert(state, object); 83 87 84 88 unsigned length = array->length(); 85 86 89 ReturnType result; 90 // If we're not an int32/double array, it's possible that converting a 91 // JSValue to a number could cause the iterator protocol to change, hence, 92 // we may need more capacity, or less. In such cases, we use the length 93 // as a proxy for the capacity we will most likely need (it's unlikely that 94 // a program is written with a valueOf that will augment the iterator protocol). 95 // If we are an int32/double array, then length is precisely the capacity we need. 87 96 if (!result.tryReserveCapacity(length)) { 88 97 // FIXME: Is the right exception to throw? … … 90 99 return { }; 91 100 } 92 101 93 102 JSC::IndexingType indexingType = array->indexingType() & JSC::IndexingShapeMask; 94 95 if (indexingType == JSC::ContiguousShape) { 96 for (unsigned i = 0; i < length; i++) { 97 auto indexValue = array->butterfly()->contiguous()[i].get(); 98 if (!indexValue) 99 result.uncheckedAppend(0); 100 else { 101 auto convertedValue = Converter<IDLType>::convert(state, indexValue); 102 RETURN_IF_EXCEPTION(scope, { }); 103 104 result.uncheckedAppend(convertedValue); 105 } 106 } 107 return result; 108 } 109 103 if (indexingType != JSC::Int32Shape && indexingType != JSC::DoubleShape) 104 return GenericConverter::convert(state, object, WTFMove(result)); 105 110 106 if (indexingType == JSC::Int32Shape) { 111 107 for (unsigned i = 0; i < length; i++) { … … 120 116 } 121 117 122 if (indexingType == JSC::DoubleShape) { 123 for (unsigned i = 0; i < length; i++) { 124 auto doubleValue = array->butterfly()->contiguousDouble()[i]; 125 if (std::isnan(doubleValue)) 126 result.uncheckedAppend(0); 127 else { 128 auto convertedValue = Converter<IDLType>::convert(state, scope, doubleValue); 129 RETURN_IF_EXCEPTION(scope, { }); 130 131 result.uncheckedAppend(convertedValue); 132 } 133 } 134 return result; 135 } 136 118 ASSERT(indexingType == JSC::DoubleShape); 137 119 for (unsigned i = 0; i < length; i++) { 138 auto indexValue = array->getDirectIndex(&state, i); 139 RETURN_IF_EXCEPTION(scope, { }); 140 141 if (!indexValue) 120 auto doubleValue = array->butterfly()->contiguousDouble()[i]; 121 if (std::isnan(doubleValue)) 142 122 result.uncheckedAppend(0); 143 123 else { 144 auto convertedValue = Converter<IDLType>::convert(state, indexValue);124 auto convertedValue = Converter<IDLType>::convert(state, scope, doubleValue); 145 125 RETURN_IF_EXCEPTION(scope, { }); 146 126 147 127 result.uncheckedAppend(convertedValue); 148 128 } … … 168 148 169 149 JSC::JSObject* object = JSC::asObject(value); 150 if (Converter<IDLType>::conversionHasSideEffects) 151 return GenericConverter::convert(state, object); 152 170 153 if (!JSC::isJSArray(object)) 171 154 return GenericConverter::convert(state, object); 172 155 173 156 JSC::JSArray* array = JSC::asArray(object); 174 if (!array-> globalObject()->isArrayIteratorProtocolFastAndNonObservable())157 if (!array->isIteratorProtocolFastAndNonObservable()) 175 158 return GenericConverter::convert(state, object); 176 159
Note: See TracChangeset
for help on using the changeset viewer.