RESOLVED FIXED 168295
Expose Symbol.toPrimitive / valueOf on Location instances
https://bugs.webkit.org/show_bug.cgi?id=168295
Summary Expose Symbol.toPrimitive / valueOf on Location instances
Attachments
Patch (7.95 KB, patch)
2017-02-14 10:20 PST, Chris Dumez
no flags
Patch (15.37 KB, patch)
2017-02-14 13:28 PST, Chris Dumez
no flags
Patch (15.96 KB, patch)
2017-02-14 13:52 PST, Chris Dumez
no flags
Patch (16.00 KB, patch)
2017-02-14 14:00 PST, Chris Dumez
no flags
Anne van Kesteren
Comment 1 2017-02-14 01:11:12 PST
The same goes for valueOf: http://web-platform.test:8000/html/browsers/history/the-location-interface/location-valueof.html. (Let me know if you need a separate bug.)
Chris Dumez
Comment 2 2017-02-14 08:42:50 PST
To create a Location object, run these steps: Let location be a new Location platform object. Perform ! location.[[DefineOwnProperty]]("valueOf", { [[Value]]: %ObjProto_valueOf%, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }). Perform ! location.[[DefineOwnProperty]](@@toPrimitive, { [[Value]]: undefined, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }). Set the value of the [[DefaultProperties]] internal slot of location to location.[[OwnPropertyKeys]](). Return location.
Chris Dumez
Comment 3 2017-02-14 08:43:20 PST
Will take a look soon.
Chris Dumez
Comment 4 2017-02-14 10:20:37 PST
Darin Adler
Comment 5 2017-02-14 10:58:34 PST
Comment on attachment 301516 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=301516&action=review > Source/WebCore/bindings/scripts/CodeGeneratorJS.pm:3430 > + push(@implContent, " auto objProtoValueOf = globalObject()->objectPrototype()->getDirect(vm, vm.propertyNames->valueOf);\n"); Please just spell out "object" instead of using "obj". > Source/WebCore/bindings/scripts/CodeGeneratorJS.pm:3432 > + push(@implContent, " putDirect(vm, vm.propertyNames->valueOf, objProtoValueOf, DontDelete | ReadOnly | DontEnum);\n"); Is this correct if the object prototype valueOf property’s value changes after creating the Location interface? Do we have test cases covered?
Geoffrey Garen
Comment 6 2017-02-14 11:02:13 PST
> Source/WebCore/bindings/scripts/CodeGeneratorJS.pm:3431 > + push(@implContent, " auto objProtoValueOf = globalObject()->objectPrototype()->getDirect(vm, vm.propertyNames->valueOf);\n"); > + push(@implContent, " ASSERT(objProtoValueOf);\n"); It's possible to change or delete this property, so I don't think this is correct. In cases where we might later need the original value of a certain property, we provide an accessor on JSGlobalObject, such as: // The following accessors return pristine values, even if a script // replaces the global object's associated property. GetterSetter* speciesGetterSetter() const { return m_speciesGetterSetter.get(); } RegExpConstructor* regExpConstructor() const { return m_regExpConstructor.get(); } ErrorConstructor* errorConstructor() const { return m_errorConstructor.get(); } ArrayConstructor* arrayConstructor() const { return m_arrayConstructor.get(); } ObjectConstructor* objectConstructor() const { return m_objectConstructor.get(); } JSPromiseConstructor* promiseConstructor() const { return m_promiseConstructor.get(); } JSInternalPromiseConstructor* internalPromiseConstructor() const { return m_internalPromiseConstructor.get(); } NativeErrorConstructor* evalErrorConstructor() const { return m_evalErrorConstructor.get(this); } NativeErrorConstructor* rangeErrorConstructor() const { return m_rangeErrorConstructor.get(); } NativeErrorConstructor* referenceErrorConstructor() const { return m_referenceErrorConstructor.get(this); } NativeErrorConstructor* syntaxErrorConstructor() const { return m_syntaxErrorConstructor.get(this); } NativeErrorConstructor* typeErrorConstructor() const { return m_typeErrorConstructor.get(); } NativeErrorConstructor* URIErrorConstructor() const { return m_URIErrorConstructor.get(this); } NullGetterFunction* nullGetterFunction() const { return m_nullGetterFunction.get(); } NullSetterFunction* nullSetterFunction() const { return m_nullSetterFunction.get(); } JSFunction* parseIntFunction() const { return m_parseIntFunction.get(); } JSFunction* evalFunction() const { return m_evalFunction.get(); } JSFunction* callFunction() const { return m_callFunction.get(); } JSFunction* applyFunction() const { return m_applyFunction.get(); } JSFunction* throwTypeErrorFunction() const { return m_throwTypeErrorFunction.get(); } JSFunction* arrayProtoToStringFunction() const { return m_arrayProtoToStringFunction.get(this); } JSFunction* arrayProtoValuesFunction() const { return m_arrayProtoValuesFunction.get(this); } JSFunction* initializePromiseFunction() const { return m_initializePromiseFunction.get(this); } JSFunction* iteratorProtocolFunction() const { return m_iteratorProtocolFunction.get(this); } JSFunction* newPromiseCapabilityFunction() const { return m_newPromiseCapabilityFunction.get(); } JSFunction* functionProtoHasInstanceSymbolFunction() const { return m_functionProtoHasInstanceSymbolFunction.get(); } JSObject* regExpProtoExecFunction() const { return m_regExpProtoExec.get(); } JSObject* regExpProtoSymbolReplaceFunction() const { return m_regExpProtoSymbolReplace.get(); } JSObject* regExpProtoGlobalGetter() const { return m_regExpProtoGlobalGetter.get(); } JSObject* regExpProtoUnicodeGetter() const { return m_regExpProtoUnicodeGetter.get(); }
Chris Dumez
Comment 7 2017-02-14 11:03:35 PST
(In reply to comment #6) > > Source/WebCore/bindings/scripts/CodeGeneratorJS.pm:3431 > > + push(@implContent, " auto objProtoValueOf = globalObject()->objectPrototype()->getDirect(vm, vm.propertyNames->valueOf);\n"); > > + push(@implContent, " ASSERT(objProtoValueOf);\n"); > > It's possible to change or delete this property, so I don't think this is > correct. > > In cases where we might later need the original value of a certain property, > we provide an accessor on JSGlobalObject, such as: > > // The following accessors return pristine values, even if a script > // replaces the global object's associated property. > > GetterSetter* speciesGetterSetter() const { return > m_speciesGetterSetter.get(); } > > RegExpConstructor* regExpConstructor() const { return > m_regExpConstructor.get(); } > > ErrorConstructor* errorConstructor() const { return > m_errorConstructor.get(); } > ArrayConstructor* arrayConstructor() const { return > m_arrayConstructor.get(); } > ObjectConstructor* objectConstructor() const { return > m_objectConstructor.get(); } > JSPromiseConstructor* promiseConstructor() const { return > m_promiseConstructor.get(); } > JSInternalPromiseConstructor* internalPromiseConstructor() const { > return m_internalPromiseConstructor.get(); } > NativeErrorConstructor* evalErrorConstructor() const { return > m_evalErrorConstructor.get(this); } > NativeErrorConstructor* rangeErrorConstructor() const { return > m_rangeErrorConstructor.get(); } > NativeErrorConstructor* referenceErrorConstructor() const { return > m_referenceErrorConstructor.get(this); } > NativeErrorConstructor* syntaxErrorConstructor() const { return > m_syntaxErrorConstructor.get(this); } > NativeErrorConstructor* typeErrorConstructor() const { return > m_typeErrorConstructor.get(); } > NativeErrorConstructor* URIErrorConstructor() const { return > m_URIErrorConstructor.get(this); } > > NullGetterFunction* nullGetterFunction() const { return > m_nullGetterFunction.get(); } > NullSetterFunction* nullSetterFunction() const { return > m_nullSetterFunction.get(); } > > JSFunction* parseIntFunction() const { return m_parseIntFunction.get(); } > > JSFunction* evalFunction() const { return m_evalFunction.get(); } > JSFunction* callFunction() const { return m_callFunction.get(); } > JSFunction* applyFunction() const { return m_applyFunction.get(); } > JSFunction* throwTypeErrorFunction() const { return > m_throwTypeErrorFunction.get(); } > JSFunction* arrayProtoToStringFunction() const { return > m_arrayProtoToStringFunction.get(this); } > JSFunction* arrayProtoValuesFunction() const { return > m_arrayProtoValuesFunction.get(this); } > JSFunction* initializePromiseFunction() const { return > m_initializePromiseFunction.get(this); } > JSFunction* iteratorProtocolFunction() const { return > m_iteratorProtocolFunction.get(this); } > JSFunction* newPromiseCapabilityFunction() const { return > m_newPromiseCapabilityFunction.get(); } > JSFunction* functionProtoHasInstanceSymbolFunction() const { return > m_functionProtoHasInstanceSymbolFunction.get(); } > JSObject* regExpProtoExecFunction() const { return > m_regExpProtoExec.get(); } > JSObject* regExpProtoSymbolReplaceFunction() const { return > m_regExpProtoSymbolReplace.get(); } > JSObject* regExpProtoGlobalGetter() const { return > m_regExpProtoGlobalGetter.get(); } > JSObject* regExpProtoUnicodeGetter() const { return > m_regExpProtoUnicodeGetter.get(); } Thanks, I'll update.
Darin Adler
Comment 8 2017-02-14 11:03:44 PST
(In reply to comment #6) > It's possible to change or delete this property, so I don't think this is > correct. We should verify this with test cases: one that changes and another that deletes the property.
Chris Dumez
Comment 9 2017-02-14 11:13:49 PST
(In reply to comment #8) > (In reply to comment #6) > > It's possible to change or delete this property, so I don't think this is > > correct. > > We should verify this with test cases: one that changes and another that > deletes the property. Will do.
Chris Dumez
Comment 10 2017-02-14 13:28:17 PST
Chris Dumez
Comment 11 2017-02-14 13:28:41 PST
Take #2.
Geoffrey Garen
Comment 12 2017-02-14 13:40:05 PST
Comment on attachment 301539 [details] Patch r=me
Keith Miller
Comment 13 2017-02-14 13:41:10 PST
Comment on attachment 301539 [details] Patch r=me too.
Mark Lam
Comment 14 2017-02-14 13:43:19 PST
Comment on attachment 301539 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=301539&action=review > Source/JavaScriptCore/runtime/JSGlobalObject.cpp:461 > + m_objectProtoValueOfFunction.set(vm, this, jsCast<JSFunction*>(objectPrototype()->getDirect(vm, vm.propertyNames->valueOf))); You also need to visit m_objectProtoValueOfFunction in JSGlobalObject::visitChildren().
Chris Dumez
Comment 15 2017-02-14 13:52:56 PST
Chris Dumez
Comment 16 2017-02-14 13:53:30 PST
(In reply to comment #14) > Comment on attachment 301539 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=301539&action=review > > > Source/JavaScriptCore/runtime/JSGlobalObject.cpp:461 > > + m_objectProtoValueOfFunction.set(vm, this, jsCast<JSFunction*>(objectPrototype()->getDirect(vm, vm.propertyNames->valueOf))); > > You also need to visit m_objectProtoValueOfFunction in > JSGlobalObject::visitChildren(). Sorry, I completely forgot to do this. This is fixed in the latest iteration.
Chris Dumez
Comment 17 2017-02-14 14:00:19 PST
Darin Adler
Comment 18 2017-02-14 14:19:49 PST
Comment on attachment 301544 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=301544&action=review > Source/JavaScriptCore/runtime/JSGlobalObject.cpp:461 > + m_objectProtoValueOfFunction.set(vm, this, jsCast<JSFunction*>(objectPrototype()->getDirect(vm, vm.propertyNames->valueOf))); It would be nicer to find a way to do this without the cast; there must be code that originally allocated this that knows it’s a JSFunction.
Chris Dumez
Comment 19 2017-02-14 14:35:57 PST
Comment on attachment 301544 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=301544&action=review >> Source/JavaScriptCore/runtime/JSGlobalObject.cpp:461 >> + m_objectProtoValueOfFunction.set(vm, this, jsCast<JSFunction*>(objectPrototype()->getDirect(vm, vm.propertyNames->valueOf))); > > It would be nicer to find a way to do this without the cast; there must be code that originally allocated this that knows it’s a JSFunction. I am not sure how to do that. We use putDirectNativeFunctionWithoutTransition() to create the JSFunction from the NativeFunction and put in on the object for us. But then, on getting via get/getDirect, we get a JSValue (Also note that I do not want to create a new JSFunction because I need to preserve identity between Object.prototype.valueOf and location.valueOf).
Darin Adler
Comment 20 2017-02-14 14:50:03 PST
Comment on attachment 301544 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=301544&action=review >>> Source/JavaScriptCore/runtime/JSGlobalObject.cpp:461 >>> + m_objectProtoValueOfFunction.set(vm, this, jsCast<JSFunction*>(objectPrototype()->getDirect(vm, vm.propertyNames->valueOf))); >> >> It would be nicer to find a way to do this without the cast; there must be code that originally allocated this that knows it’s a JSFunction. > > I am not sure how to do that. We use putDirectNativeFunctionWithoutTransition() to create the JSFunction from the NativeFunction and put in on the object for us. > But then, on getting via get/getDirect, we get a JSValue (Also note that I do not want to create a new JSFunction because I need to preserve identity between Object.prototype.valueOf and location.valueOf). One idea: Could add a JSFunction* return value to putDirectNativeFunctionWithoutTransition, ignored by other callers, but used in this case.
Chris Dumez
Comment 21 2017-02-14 14:53:28 PST
(In reply to comment #20) > Comment on attachment 301544 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=301544&action=review > > >>> Source/JavaScriptCore/runtime/JSGlobalObject.cpp:461 > >>> + m_objectProtoValueOfFunction.set(vm, this, jsCast<JSFunction*>(objectPrototype()->getDirect(vm, vm.propertyNames->valueOf))); > >> > >> It would be nicer to find a way to do this without the cast; there must be code that originally allocated this that knows it’s a JSFunction. > > > > I am not sure how to do that. We use putDirectNativeFunctionWithoutTransition() to create the JSFunction from the NativeFunction and put in on the object for us. > > But then, on getting via get/getDirect, we get a JSValue (Also note that I do not want to create a new JSFunction because I need to preserve identity between Object.prototype.valueOf and location.valueOf). > > One idea: Could add a JSFunction* return value to > putDirectNativeFunctionWithoutTransition, ignored by other callers, but used > in this case. putDirectNativeFunctionWithoutTransition() is called inside ObjectPrototype::finishCreation(). I need access to it in the JSGlobalObject after the ObjectPrototype has been constructed.
Chris Dumez
Comment 22 2017-02-14 14:54:55 PST
(In reply to comment #21) > (In reply to comment #20) > > Comment on attachment 301544 [details] > > Patch > > > > View in context: > > https://bugs.webkit.org/attachment.cgi?id=301544&action=review > > > > >>> Source/JavaScriptCore/runtime/JSGlobalObject.cpp:461 > > >>> + m_objectProtoValueOfFunction.set(vm, this, jsCast<JSFunction*>(objectPrototype()->getDirect(vm, vm.propertyNames->valueOf))); > > >> > > >> It would be nicer to find a way to do this without the cast; there must be code that originally allocated this that knows it’s a JSFunction. > > > > > > I am not sure how to do that. We use putDirectNativeFunctionWithoutTransition() to create the JSFunction from the NativeFunction and put in on the object for us. > > > But then, on getting via get/getDirect, we get a JSValue (Also note that I do not want to create a new JSFunction because I need to preserve identity between Object.prototype.valueOf and location.valueOf). > > > > One idea: Could add a JSFunction* return value to > > putDirectNativeFunctionWithoutTransition, ignored by other callers, but used > > in this case. > > putDirectNativeFunctionWithoutTransition() is called inside > ObjectPrototype::finishCreation(). I need access to it in the JSGlobalObject > after the ObjectPrototype has been constructed. c.f. void ObjectPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) { Base::finishCreation(vm); ASSERT(inherits(vm, info())); // ... JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->valueOf, objectProtoFuncValueOf, DontEnum, 0); // ... }
Mark Lam
Comment 23 2017-02-14 15:15:58 PST
Comment on attachment 301544 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=301544&action=review >>>>>> Source/JavaScriptCore/runtime/JSGlobalObject.cpp:461 >>>>>> + m_objectProtoValueOfFunction.set(vm, this, jsCast<JSFunction*>(objectPrototype()->getDirect(vm, vm.propertyNames->valueOf))); >>>>> >>>>> It would be nicer to find a way to do this without the cast; there must be code that originally allocated this that knows it’s a JSFunction. >>>> >>>> I am not sure how to do that. We use putDirectNativeFunctionWithoutTransition() to create the JSFunction from the NativeFunction and put in on the object for us. >>>> But then, on getting via get/getDirect, we get a JSValue (Also note that I do not want to create a new JSFunction because I need to preserve identity between Object.prototype.valueOf and location.valueOf). >>> >>> One idea: Could add a JSFunction* return value to putDirectNativeFunctionWithoutTransition, ignored by other callers, but used in this case. >> >> putDirectNativeFunctionWithoutTransition() is called inside ObjectPrototype::finishCreation(). I need access to it in the JSGlobalObject after the ObjectPrototype has been constructed. > > c.f. > void ObjectPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) > { > Base::finishCreation(vm); > ASSERT(inherits(vm, info())); > // ... > JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->valueOf, objectProtoFuncValueOf, DontEnum, 0); > // ... > } Some details to consider: 1. JSGlobalObject::init() (this function) doesn't itself call putDirectNativeFunctionWithoutTransition() to create the Function that it is trying to cache. 2. JSGlobalObject::init() instantiates m_objectPrototype above. It's the ObjectPrototype factory that creates the function. 3. It would be strange for ObjectPrototype::finishCreation() to cache the function in the globalObject. This creates a tighter coupling between ObjectPrototype and JSGlobalObject. For these reasons, I think the way Chris implemented it is just fine. I suggest we move forward with this patch.
WebKit Commit Bot
Comment 24 2017-02-15 10:20:40 PST
Comment on attachment 301544 [details] Patch Rejecting attachment 301544 [details] from commit-queue. Failed to run "['/Volumes/Data/EWS/WebKit/Tools/Scripts/webkit-patch', '--status-host=webkit-queues.webkit.org', '--bot-id=webkit-cq-02', 'land-attachment', '--force-clean', '--non-interactive', '--parent-command=commit-queue', 301544, '--port=mac']" exit_code: 1 cwd: /Volumes/Data/EWS/WebKit Last 500 characters of output: ckout/scm/scm.py", line 77, in run decode_output=decode_output) File "/Volumes/Data/EWS/WebKit/Tools/Scripts/webkitpy/common/system/executive.py", line 443, in run_command output = output.decode(self._child_process_encoding()) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings/utf_8.py", line 16, in decode return codecs.utf_8_decode(input, errors, True) UnicodeDecodeError: 'utf8' codec can't decode byte 0xad in position 142: invalid start byte Full output: http://webkit-queues.webkit.org/results/3122689
Chris Dumez
Comment 25 2017-02-15 10:25:15 PST
Comment on attachment 301544 [details] Patch Clearing flags on attachment: 301544 Committed r212378: <http://trac.webkit.org/changeset/212378>
Chris Dumez
Comment 26 2017-02-15 10:25:24 PST
All reviewed patches have been landed. Closing bug.
Note You need to log in before you can comment on or make changes to this bug.