RESOLVED FIXED 211020
[WebIDL] Interface prototype objects should define @@toStringTag
https://bugs.webkit.org/show_bug.cgi?id=211020
Summary [WebIDL] Interface prototype objects should define @@toStringTag
Alexey Shvayka
Reported 2020-04-25 05:33:28 PDT
WebIDL spec was recently updated (https://github.com/heycam/webidl/pull/357) to define Symbol.toStringTag on interface prototype objects. This change aligns WebIDL with ECMA-262 built-ins and Blink's implementation that have been shipping since Q2 2016. Gecko have also expressed implementation commitment. r253855 introduced class strings (https://heycam.github.io/webidl/#dfn-class-string) for iterator prototypes only.
Attachments
Patch (1.81 MB, patch)
2020-04-25 05:56 PDT, Alexey Shvayka
no flags
Patch (1.81 MB, patch)
2020-04-25 06:20 PDT, Alexey Shvayka
no flags
Patch (229.34 KB, patch)
2020-04-25 12:40 PDT, Alexey Shvayka
no flags
Patch (251.50 KB, patch)
2020-04-25 12:47 PDT, Alexey Shvayka
no flags
Patch (273.54 KB, patch)
2020-04-25 14:46 PDT, Alexey Shvayka
no flags
Patch (327.88 KB, patch)
2020-04-27 09:19 PDT, Alexey Shvayka
no flags
Patch (395.71 KB, patch)
2020-04-28 10:50 PDT, Alexey Shvayka
no flags
Patch (395.94 KB, patch)
2020-04-28 13:47 PDT, Alexey Shvayka
no flags
Patch (393.83 KB, patch)
2020-04-30 17:27 PDT, Alexey Shvayka
no flags
Alexey Shvayka
Comment 1 2020-04-25 05:56:48 PDT
Alexey Shvayka
Comment 2 2020-04-25 06:20:16 PDT
Created attachment 397560 [details] Patch Add JSC namespace in JSDOMWindowProperties.h.
Alexey Shvayka
Comment 3 2020-04-25 12:40:40 PDT
Created attachment 397574 [details] Patch Drop WebAssembly[Symbol.toStringTag], fix global objects prototypes, and adjust tests.
Alexey Shvayka
Comment 4 2020-04-25 12:47:27 PDT
Created attachment 397575 [details] Patch Add missing "$" in CodeGeneratorJS.pm.
Alexey Shvayka
Comment 5 2020-04-25 14:46:05 PDT
Created attachment 397585 [details] Patch Adjust remaining tests and improve ChangeLog.
Darin Adler
Comment 6 2020-04-26 22:16:38 PDT
Comment on attachment 397585 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=397585&action=review Looks great. Is there a more efficient way of getting this behavior without explicitly putting the property in the prototype object at runtime? The change of the name seems fine, but I am a little sad to see all the putDirectWithoutTransition calls. Not really up to date enough on the latest about performance of the JavaScript runtime, so not really sure if this is a bad thing or not. I also have feelings about code being written out over and over again by the code generator. I’d like to see us put a function somewhere that does this without always repeating toStringTagSymbol and DontEnum and ReadOnly, etc. Once we start generating code, it’s tempting to write out a lot, but I would have wanted the generated code to be more like this: setUpToStringTag(vm, "CSSValueList"_s); That function would be in a plain old C++ source file. > Source/WebCore/bindings/scripts/CodeGeneratorJS.pm:4381 > + push(@implContent, " putDirectWithoutTransition(vm, vm.propertyNames->toStringTagSymbol, jsNontrivialString(vm, \"${visibleInterfaceName}\"_s), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::ReadOnly);\n"); This assumes that the visibleInterfaceName will never be a single character. If it is a single character, then we need to call jsString rather than jsNontrivialString. Probably a safe assumption -- what’s the chance of a single character DOM class name? -- but maybe we should put something in the perl code to check for this so it’s a compile time failure rather than a runtime failure. > Source/WebCore/bindings/scripts/CodeGeneratorJS.pm:7321 > if (IsGlobalInterface($interface)) { > $structureFlags{"JSC::HasStaticPropertyTable"} = 1; Could make this a one-liner with perl's suffix-style if statement. > LayoutTests/ChangeLog:36 > + * resources/idlharness.js: Remove asserts that were are commented out in upstream. small mistake: "were are"
Alexey Shvayka
Comment 7 2020-04-27 09:19:58 PDT
Created attachment 397689 [details] Patch Add assert before jsNontrivialString(), use suffix-style if statement, tweak ChangeLog, rebase on updated cross-realm tests expectations.
Alexey Shvayka
Comment 8 2020-04-27 09:20:06 PDT
(In reply to Darin Adler from comment #6) I appreciate the review and comprehensive comments! > Is there a more efficient way of getting this behavior without explicitly > putting the property in the prototype object at runtime? The change of the > name seems fine, but I am a little sad to see all the > putDirectWithoutTransition calls. Not really up to date enough on the latest > about performance of the JavaScript runtime, so not really sure if this is a > bad thing or not. Given that WebIDL requires @@toStringTag as own property on interface prototypes (https://heycam.github.io/webidl/#dfn-class-string) to enable developers checking for cross-realm instances via `obj[Symbol.toStringTag]` instead of `Object.prototype.toString.call(obj).slice(8, -1)`, there is not much we could do. If interface prototypes extended a common class, we could have define @@toStringTag as CustomValue though. > I also have feelings about code being written out over and over again by the > code generator. I’d like to see us put a function somewhere that does this > without always repeating toStringTagSymbol and DontEnum and ReadOnly, etc. > Once we start generating code, it’s tempting to write out a lot, but I would > have wanted the generated code to be more like this: I agree that code generator will benefit from changes like this and code removal in general (I have a change in mind that would simplify callback interfaces a bit), yet I am not sure it worth extracting @@toStringTag due to its size/complexity, and to keep it consistent with other well-known symbols we emit in CodeGeneratorJS.pm.
Darin Adler
Comment 9 2020-04-27 09:27:37 PDT
(In reply to Alexey Shvayka from comment #8) > I am not sure it worth extracting @@toStringTag due to its > size/complexity, and to keep it consistent with other well-known symbols we > emit in CodeGeneratorJS.pm. My feedback also applies to other well-known symbols we emit in CodeGeneratorJS.pm. I think we should be putting as much of the code as possible in C++ source files rather than in the perl code generator as long as it doesn’t cause problems for complexity, performance, or clarity.
Alexey Shvayka
Comment 10 2020-04-27 11:24:38 PDT
(In reply to Darin Adler from comment #6) > setUpToStringTag(vm, "CSSValueList"_s); > > That function would be in a plain old C++ source file. Since WebIDL interface prototypes extend JSNonFinalObject and are fully generated by Perl script, we can't define setUpToStringTag() as a method and would have to pass an additional argument: `setUpToStringTag(vm, this, "CSSValueList"_s);`. It doesn't feel right to me (in terms of OOP) that a function merely manipulates its object parameter. Furthermore, I believe it is important to use putDirectWithoutTransition() performance-wise, which does not seem like a good fit inside a helper function (I could not `grep` any usages) as it expects certain object state that we can't guarantee outside of finishCreation().
Darin Adler
Comment 11 2020-04-27 11:32:37 PDT
(In reply to Alexey Shvayka from comment #10) > Since WebIDL interface prototypes extend JSNonFinalObject and are fully > generated by Perl script, we can't define setUpToStringTag() as a method and > would have to pass an additional argument: `setUpToStringTag(vm, this, > "CSSValueList"_s);`. It doesn't feel right to me (in terms of OOP) that a > function merely manipulates its object parameter. OK, but this seems like a straw man. Why can’t we create an abstract base class that’s derived from JSNonFinalObject instead of deriving directly from it? > Furthermore, I believe it is important to use putDirectWithoutTransition() > performance-wise, which does not seem like a good fit inside a helper > function (I could not `grep` any usages) as it expects certain object state > that we can't guarantee outside of finishCreation(). To me that just means we need an appropriately specific function name.
Alexey Shvayka
Comment 12 2020-04-27 13:17:28 PDT
(In reply to Darin Adler from comment #11) > Why can’t we create an abstract base class that’s derived from JSNonFinalObject instead of deriving directly from it? With abstract base class, we can set correct `className` and define @@toStringTag without additional method: void JSPrototypeAbstract::finishCreation(VM& vm) { Base::finishCreation(vm); ASSERT(inherits(vm, info())); putDirectWithoutTransition(vm, vm.propertyNames->toStringTagSymbol, jsNontrivialString(vm, info()->className), PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly); } > To me that just means we need an appropriately specific function name. Alternatively, we can define JSObject::putToStringTagWithoutTransition(VM&), which will also expect correctly set `className`, and use it across Source/JSC (~30 call sites). This helper will ensure not only correct descriptor attributes and usage of jsNontrivialString(), but also a consistent `className` value.
Darin Adler
Comment 13 2020-04-27 13:28:35 PDT
I think those are both good ideas. Thanks for considering some of these approaches that are a little heavier on code outside the code generator, and lighter on code inside it.
Alexey Shvayka
Comment 14 2020-04-28 10:50:27 PDT
Created attachment 397855 [details] Patch Extract JSC_TO_STRING_TAG_WITHOUT_TRANSITION() macro.
Darin Adler
Comment 15 2020-04-28 11:08:32 PDT
Comment on attachment 397855 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=397855&action=review Looks good. > Source/JavaScriptCore/runtime/JSObject.h:1604 > +#define JSC_TO_STRING_TAG_WITHOUT_TRANSITION() \ > + putDirectWithoutTransition(vm, vm.propertyNames->toStringTagSymbol, \ > + jsNontrivialString(vm, info()->className), PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly) Why is this a macro rather than a function? The other macros in this file have arguments, and so I see why they are not just functions, but this seems different.
Alexey Shvayka
Comment 16 2020-04-28 13:13:39 PDT
(In reply to Darin Adler from comment #15) > Why is this a macro rather than a function? The other macros in this file > have arguments, and so I see why they are not just functions, but this seems > different. It is a macro so we could do `info()->className` without resorting to methodTable and dynamic dispatch. If it was a function, it would not perfectly fit in with other putDirect* methods signature-wise as well IMO.
Darin Adler
Comment 17 2020-04-28 13:38:16 PDT
(In reply to Alexey Shvayka from comment #16) > It is a macro so we could do `info()->className` Got it.
Alexey Shvayka
Comment 18 2020-04-28 13:47:28 PDT
Created attachment 397879 [details] Patch Adjust expectations for class-string-interface.any.js WPT test.
Alexey Shvayka
Comment 19 2020-04-30 17:27:29 PDT
Created attachment 398126 [details] Patch dom/nodes/Document-createEvent.html is obsolete, revert expectations change.
Alexey Shvayka
Comment 20 2020-05-01 02:33:26 PDT
Comment on attachment 398126 [details] Patch stress/array-buffer-view-watchpoint-can-be-fired-in-really-add-in-dfg.js failure on ARMv7 looks unrelated: https://bugs.webkit.org/show_bug.cgi?id=210717
EWS
Comment 21 2020-05-01 02:48:41 PDT
Committed r260992: <https://trac.webkit.org/changeset/260992> All reviewed patches have been landed. Closing bug and clearing flags on attachment 398126 [details].
Radar WebKit Bug Importer
Comment 22 2020-05-01 02:49:19 PDT
Alexey Shvayka
Comment 23 2020-05-01 04:26:12 PDT
Darin Adler
Comment 24 2020-05-01 09:50:58 PDT
Are there any places where we can remove code that used to implement strings like "[object Window]" that are now correctly inherited from the prototype?
Alexey Shvayka
Comment 25 2020-05-01 10:55:36 PDT
(In reply to Darin Adler from comment #24) > Are there any places where we can remove code that used to implement strings > like "[object Window]" that are now correctly inherited from the prototype? Not exactly remove, but rather simplify: strings like "[object Window]" for instances are implemented by JSObject::toStringName(), which returns `className`. Now that strings are inherited from prototypes, we can just return "Object" or "Function" from JSObject::toStringName(), making sure pre-ES6 classes redefine it as per steps 5-13 of https://tc39.es/ecma262/#sec-object.prototype.tostring. Once we do this, remaining tests cases in wpt/WebIDL/ecmascript-binding/class-string-interface.any.js will be fixed, as well as few test262 failures. Please follow the progress at https://bugs.webkit.org/show_bug.cgi?id=199138.
Note You need to log in before you can comment on or make changes to this bug.