Changeset 195878 in webkit
- Timestamp:
- Jan 29, 2016 6:45:25 PM (8 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 1 added
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r195877 r195878 1 2016-01-29 Keith Miller <keith_miller@apple.com> 2 3 Array.prototype native functions should use Symbol.species to construct the result 4 https://bugs.webkit.org/show_bug.cgi?id=153660 5 6 Reviewed by Saam Barati. 7 8 This patch adds support for Symbol.species in the Array.prototype native functions. 9 We make an optimization to avoid regressions on some benchmarks by using an 10 adaptive watchpoint to check if Array.prototype.constructor is ever changed. 11 12 * runtime/ArrayPrototype.cpp: 13 (JSC::putLength): 14 (JSC::setLength): 15 (JSC::speciesConstructArray): 16 (JSC::arrayProtoFuncConcat): 17 (JSC::arrayProtoFuncSlice): 18 (JSC::arrayProtoFuncSplice): 19 (JSC::ArrayPrototype::setConstructor): 20 (JSC::ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint): 21 (JSC::ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire): 22 * runtime/ArrayPrototype.h: 23 (JSC::ArrayPrototype::didChangeConstructorProperty): 24 * runtime/ConstructData.cpp: 25 (JSC::construct): 26 * runtime/ConstructData.h: 27 * runtime/JSGlobalObject.cpp: 28 (JSC::JSGlobalObject::init): 29 * tests/es6.yaml: 30 * tests/stress/array-species-functions.js: Added. 31 (Symbol.species): 32 (funcThrows): 33 (test.species): 34 (test): 35 1 36 2016-01-29 Filip Pizlo <fpizlo@apple.com> 2 37 -
trunk/Source/JavaScriptCore/runtime/ArrayPrototype.cpp
r195528 r195878 25 25 #include "ArrayPrototype.h" 26 26 27 #include "AdaptiveInferredPropertyValueWatchpointBase.h" 28 #include "ArrayConstructor.h" 27 29 #include "BuiltinNames.h" 28 30 #include "ButterflyInlines.h" … … 156 158 } 157 159 158 static void putLength(ExecState* exec, JSObject* obj, JSValue value)160 static ALWAYS_INLINE void putLength(ExecState* exec, JSObject* obj, JSValue value) 159 161 { 160 162 PutPropertySlot slot(obj); 161 163 obj->methodTable()->put(obj, exec, exec->propertyNames().length, value, slot); 164 } 165 166 static ALWAYS_INLINE void setLength(ExecState* exec, JSObject* obj, unsigned value) 167 { 168 if (isJSArray(obj)) 169 jsCast<JSArray*>(obj)->setLength(exec, value); 170 putLength(exec, obj, jsNumber(value)); 171 } 172 173 enum class SpeciesConstructResult { 174 FastPath, 175 Exception, 176 CreatedObject 177 }; 178 179 static ALWAYS_INLINE std::pair<SpeciesConstructResult, JSObject*> speciesConstructArray(ExecState* exec, JSObject* thisObject, unsigned length) 180 { 181 // ECMA 9.4.2.3: https://tc39.github.io/ecma262/#sec-arrayspeciescreate 182 JSValue constructor = jsUndefined(); 183 if (LIKELY(isJSArray(thisObject))) { 184 // Fast path in the normal case where the user has not set an own constructor and the Array.prototype.constructor is normal. 185 // We need prototype check for subclasses of Array, which are Array objects but have a different prototype by default. 186 if (LIKELY(!thisObject->hasCustomProperties() 187 && thisObject->globalObject()->arrayPrototype() == thisObject->prototype() 188 && !thisObject->globalObject()->arrayPrototype()->didChangeConstructorProperty())) 189 return std::make_pair(SpeciesConstructResult::FastPath, nullptr); 190 191 constructor = thisObject->get(exec, exec->propertyNames().constructor); 192 if (exec->hadException()) 193 return std::make_pair(SpeciesConstructResult::Exception, nullptr); 194 if (constructor.isConstructor()) { 195 JSObject* constructorObject = jsCast<JSObject*>(constructor); 196 if (exec->lexicalGlobalObject() != constructorObject->globalObject()) 197 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);; 198 } 199 if (constructor.isObject()) { 200 constructor = constructor.get(exec, exec->propertyNames().speciesSymbol); 201 if (exec->hadException()) 202 return std::make_pair(SpeciesConstructResult::Exception, nullptr); 203 if (constructor.isNull()) 204 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);; 205 } 206 } 207 if (constructor.isUndefined()) 208 return std::make_pair(SpeciesConstructResult::FastPath, nullptr);; 209 210 MarkedArgumentBuffer args; 211 args.append(jsNumber(length)); 212 JSObject* newObject = construct(exec, constructor, args, "Species construction did not get a valid constructor"); 213 if (exec->hadException()) 214 return std::make_pair(SpeciesConstructResult::Exception, nullptr); 215 return std::make_pair(SpeciesConstructResult::CreatedObject, newObject); 162 216 } 163 217 … … 533 587 Checked<unsigned, RecordOverflow> finalArraySize = 0; 534 588 589 // We need to do species construction before geting the rest of the elements. 590 std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, curArg.getObject(), 0); 591 if (speciesResult.first == SpeciesConstructResult::Exception) 592 return JSValue::encode(jsUndefined()); 593 535 594 JSArray* currentArray = nullptr; 536 595 JSArray* previousArray = nullptr; … … 553 612 return JSValue::encode(throwOutOfMemoryError(exec)); 554 613 555 if ( argCount == 1 && previousArray && currentArray && finalArraySize.unsafeGet() < MIN_SPARSE_ARRAY_INDEX) {614 if (speciesResult.first == SpeciesConstructResult::FastPath && argCount == 1 && previousArray && currentArray && finalArraySize.unsafeGet() < MIN_SPARSE_ARRAY_INDEX) { 556 615 IndexingType type = JSArray::fastConcatType(exec->vm(), *previousArray, *currentArray); 557 616 if (type != NonArray) … … 559 618 } 560 619 561 JSArray* arr = constructEmptyArray(exec, nullptr, finalArraySize.unsafeGet()); 562 if (exec->hadException()) 563 return JSValue::encode(jsUndefined()); 620 ASSERT(speciesResult.first != SpeciesConstructResult::Exception); 621 622 JSObject* result; 623 if (speciesResult.first == SpeciesConstructResult::CreatedObject) 624 result = speciesResult.second; 625 else { 626 // We add the newTarget because the compiler gets confused between 0 being a number and a pointer. 627 result = constructEmptyArray(exec, nullptr, 0, JSValue()); 628 } 564 629 565 630 curArg = thisValue.toObject(exec); … … 576 641 return JSValue::encode(jsUndefined()); 577 642 if (v) 578 arr->putDirectIndex(exec, n, v);643 result->putDirectIndex(exec, n, v); 579 644 n++; 580 645 } 581 646 } else { 582 arr->putDirectIndex(exec, n, curArg);647 result->putDirectIndex(exec, n, curArg); 583 648 n++; 584 649 } … … 587 652 curArg = exec->uncheckedArgument(i); 588 653 } 589 arr->setLength(exec, n);590 return JSValue::encode( arr);654 setLength(exec, result, n); 655 return JSValue::encode(result); 591 656 } 592 657 … … 758 823 unsigned end = argumentClampedIndexFromStartOrEnd(exec, 1, length, length); 759 824 760 if (isJSArray(thisObj)) { 825 std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, end - begin); 826 // We can only get an exception if we call some user function. 827 if (UNLIKELY(speciesResult.first == SpeciesConstructResult::Exception)) 828 return JSValue::encode(jsUndefined()); 829 830 if (LIKELY(speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj))) { 761 831 if (JSArray* result = asArray(thisObj)->fastSlice(*exec, begin, end - begin)) 762 832 return JSValue::encode(result); 763 833 } 764 834 765 JSArray* result = constructEmptyArray(exec, nullptr, end - begin); 835 JSObject* result; 836 if (speciesResult.first == SpeciesConstructResult::CreatedObject) 837 result = speciesResult.second; 838 else 839 result = constructEmptyArray(exec, nullptr, end - begin); 766 840 767 841 unsigned n = 0; … … 773 847 result->putDirectIndex(exec, n, v); 774 848 } 775 result->setLength(exec, n);849 setLength(exec, result, n); 776 850 return JSValue::encode(result); 777 851 } … … 787 861 if (exec->hadException()) 788 862 return JSValue::encode(jsUndefined()); 789 790 if (!exec->argumentCount()) 791 return JSValue::encode(constructEmptyArray(exec, nullptr)); 863 864 if (!exec->argumentCount()) { 865 std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, 0); 866 if (speciesResult.first == SpeciesConstructResult::Exception) 867 return JSValue::encode(jsUndefined()); 868 869 JSObject* result; 870 if (speciesResult.first == SpeciesConstructResult::CreatedObject) 871 result = speciesResult.second; 872 else 873 result = constructEmptyArray(exec, nullptr); 874 875 setLength(exec, result, 0); 876 return JSValue::encode(result); 877 } 792 878 793 879 unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 0, length); … … 804 890 } 805 891 806 JSArray* result = nullptr; 807 808 if (isJSArray(thisObj)) 892 std::pair<SpeciesConstructResult, JSObject*> speciesResult = speciesConstructArray(exec, thisObj, deleteCount); 893 if (speciesResult.first == SpeciesConstructResult::Exception) 894 return JSValue::encode(jsUndefined()); 895 896 JSObject* result = nullptr; 897 if (speciesResult.first == SpeciesConstructResult::FastPath && isJSArray(thisObj)) 809 898 result = asArray(thisObj)->fastSlice(*exec, begin, deleteCount); 810 899 811 900 if (!result) { 812 result = JSArray::tryCreateUninitialized(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), deleteCount); 813 if (!result) 814 return JSValue::encode(throwOutOfMemoryError(exec)); 901 if (speciesResult.first == SpeciesConstructResult::CreatedObject) 902 result = speciesResult.second; 903 else { 904 result = JSArray::tryCreateUninitialized(vm, exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(ArrayWithUndecided), deleteCount); 905 if (!result) 906 return JSValue::encode(throwOutOfMemoryError(exec)); 907 } 815 908 816 909 for (unsigned k = 0; k < deleteCount; ++k) { … … 838 931 } 839 932 840 putLength(exec, thisObj, jsNumber(length - deleteCount + additionalArgs));933 setLength(exec, thisObj, length - deleteCount + additionalArgs); 841 934 return JSValue::encode(result); 842 935 } … … 944 1037 } 945 1038 1039 // -------------------- ArrayPrototype.constructor Watchpoint ------------------ 1040 1041 class ArrayPrototypeAdaptiveInferredPropertyWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase { 1042 public: 1043 typedef AdaptiveInferredPropertyValueWatchpointBase Base; 1044 ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition&, ArrayPrototype*); 1045 1046 private: 1047 virtual void handleFire(const FireDetail&) override; 1048 1049 ArrayPrototype* m_arrayPrototype; 1050 }; 1051 1052 void ArrayPrototype::setConstructor(VM& vm, JSObject* constructorProperty, unsigned attributes) 1053 { 1054 putDirectWithoutTransition(vm, vm.propertyNames->constructor, constructorProperty, attributes); 1055 1056 PropertyOffset offset = this->structure()->get(vm, vm.propertyNames->constructor); 1057 ASSERT(isValidOffset(offset)); 1058 this->structure()->startWatchingPropertyForReplacements(vm, offset); 1059 1060 ObjectPropertyCondition condition = ObjectPropertyCondition::equivalence(vm, this, this, vm.propertyNames->constructor.impl(), constructorProperty); 1061 ASSERT(condition.isWatchable()); 1062 1063 m_constructorWatchpoint = std::make_unique<ArrayPrototypeAdaptiveInferredPropertyWatchpoint>(condition, this); 1064 m_constructorWatchpoint->install(); 1065 } 1066 1067 ArrayPrototypeAdaptiveInferredPropertyWatchpoint::ArrayPrototypeAdaptiveInferredPropertyWatchpoint(const ObjectPropertyCondition& key, ArrayPrototype* prototype) 1068 : Base(key) 1069 , m_arrayPrototype(prototype) 1070 { 1071 } 1072 1073 void ArrayPrototypeAdaptiveInferredPropertyWatchpoint::handleFire(const FireDetail& detail) 1074 { 1075 StringPrintStream out; 1076 out.print("ArrayPrototype adaption of ", key(), " failed: ", detail); 1077 1078 StringFireDetail stringDetail(out.toCString().data()); 1079 1080 m_arrayPrototype->m_didChangeConstructorProperty = true; 1081 } 1082 946 1083 } // namespace JSC -
trunk/Source/JavaScriptCore/runtime/ArrayPrototype.h
r190429 r195878 27 27 namespace JSC { 28 28 29 class ArrayPrototypeAdaptiveInferredPropertyWatchpoint; 30 29 31 class ArrayPrototype : public JSArray { 30 32 private: … … 43 45 } 44 46 47 void setConstructor(VM&, JSObject* constructorProperty, unsigned attributes); 48 49 bool didChangeConstructorProperty() const { return m_didChangeConstructorProperty; } 50 45 51 protected: 46 52 void finishCreation(VM&, JSGlobalObject*); 53 54 private: 55 // This bit is set if any user modifies the constructor property Array.prototype. This is used to optimize species creation for JSArrays. 56 friend ArrayPrototypeAdaptiveInferredPropertyWatchpoint; 57 std::unique_ptr<ArrayPrototypeAdaptiveInferredPropertyWatchpoint> m_constructorWatchpoint; 58 bool m_didChangeConstructorProperty = false; 47 59 }; 48 60 -
trunk/Source/JavaScriptCore/runtime/ConstructData.cpp
r194242 r195878 36 36 namespace JSC { 37 37 38 JSObject* construct(ExecState* exec, JSValue constructorObject, const ArgList& args, const String& errorMessage) 39 { 40 ConstructData constructData; 41 ConstructType constructType = getConstructData(constructorObject, constructData); 42 if (constructType == ConstructTypeNone) 43 return throwTypeError(exec, errorMessage); 44 45 return construct(exec, constructorObject, constructType, constructData, args, constructorObject); 46 } 47 48 38 49 JSObject* construct(ExecState* exec, JSValue constructorObject, ConstructType constructType, const ConstructData& constructData, const ArgList& args, JSValue newTarget) 39 50 { -
trunk/Source/JavaScriptCore/runtime/ConstructData.h
r194242 r195878 57 57 }; 58 58 59 // Convenience wrapper so you don't need to deal with CallData and CallType unless you are going to use them. 60 JSObject* construct(ExecState*, JSValue functionObject, const ArgList&, const String& errorMessage); 59 61 JS_EXPORT_PRIVATE JSObject* construct(ExecState*, JSValue constructor, ConstructType, const ConstructData&, const ArgList&, JSValue newTarget); 60 62 -
trunk/Source/JavaScriptCore/runtime/JSCJSValue.h
r194175 r195878 220 220 bool isEmpty() const; 221 221 bool isFunction() const; 222 bool isConstructor() const; 222 223 bool isUndefined() const; 223 224 bool isNull() const; -
trunk/Source/JavaScriptCore/runtime/JSCJSValueInlines.h
r194996 r195878 683 683 } 684 684 685 // FIXME: We could do this in a smarter way. See: https://bugs.webkit.org/show_bug.cgi?id=153670 686 inline bool JSValue::isConstructor() const 687 { 688 if (isFunction()) { 689 ConstructData data; 690 return getConstructData(*this, data) != ConstructTypeNone; 691 } 692 return false; 693 } 694 685 695 // this method is here to be after the inline declaration of JSCell::inherits 686 696 inline bool JSValue::inherits(const ClassInfo* classInfo) const -
trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp
r195799 r195878 405 405 406 406 JSCell* functionConstructor = FunctionConstructor::create(vm, FunctionConstructor::createStructure(vm, this, m_functionPrototype.get()), m_functionPrototype.get()); 407 JS Cell* arrayConstructor = ArrayConstructor::create(vm, ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_arrayPrototype.get(), speciesGetterSetter);407 JSObject* arrayConstructor = ArrayConstructor::create(vm, ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_arrayPrototype.get(), speciesGetterSetter); 408 408 409 409 m_regExpConstructor.set(vm, this, RegExpConstructor::create(vm, RegExpConstructor::createStructure(vm, this, m_functionPrototype.get()), m_regExpPrototype.get(), speciesGetterSetter)); … … 440 440 m_objectPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, objectConstructor, DontEnum); 441 441 m_functionPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, functionConstructor, DontEnum); 442 m_arrayPrototype-> putDirectWithoutTransition(vm, vm.propertyNames->constructor, arrayConstructor, DontEnum);442 m_arrayPrototype->setConstructor(vm, arrayConstructor, DontEnum); 443 443 m_regExpPrototype->putDirectWithoutTransition(vm, vm.propertyNames->constructor, m_regExpConstructor.get(), DontEnum); 444 444 -
trunk/Source/JavaScriptCore/tests/es6.yaml
r195581 r195878 718 718 cmd: runES6 :normal 719 719 - path: es6/Array_is_subclassable_Array.prototype.concat.js 720 cmd: runES6 : fail720 cmd: runES6 :normal 721 721 - path: es6/Array_is_subclassable_Array.prototype.filter.js 722 722 cmd: runES6 :fail … … 724 724 cmd: runES6 :fail 725 725 - path: es6/Array_is_subclassable_Array.prototype.slice.js 726 cmd: runES6 : fail726 cmd: runES6 :normal 727 727 - path: es6/Array_is_subclassable_Array.prototype.splice.js 728 cmd: runES6 : fail728 cmd: runES6 :normal 729 729 - path: es6/Array_is_subclassable_correct_prototype_chain.js 730 730 cmd: runES6 :normal … … 1196 1196 cmd: runES6 :fail 1197 1197 - path: es6/well-known_symbols_Symbol.species_Array.prototype.concat.js 1198 cmd: runES6 : fail1198 cmd: runES6 :normal 1199 1199 - path: es6/well-known_symbols_Symbol.species_Array.prototype.filter.js 1200 1200 cmd: runES6 :fail … … 1202 1202 cmd: runES6 :fail 1203 1203 - path: es6/well-known_symbols_Symbol.species_Array.prototype.slice.js 1204 cmd: runES6 : fail1204 cmd: runES6 :normal 1205 1205 - path: es6/well-known_symbols_Symbol.species_Array.prototype.splice.js 1206 cmd: runES6 : fail1206 cmd: runES6 :normal 1207 1207 - path: es6/well-known_symbols_Symbol.species_existence.js 1208 1208 cmd: runES6 :normal
Note: See TracChangeset
for help on using the changeset viewer.