New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expand JS error serialization #5749
base: main
Are you sure you want to change the base?
Conversation
Did you consider using deep? Would that also cover cases such as |
Since these properties are non-enumerable, that would not work. |
I guess we could add a branch to that algorithm though and keep a list of desired properties around in a different way. |
That seems pretty indirect, compared to just serializing AggregateError-specific data in the AggregateError branch. |
It is a bit, but if we want |
I don't think so. In particular the main characteristic of deep is that it iterates over an unbounded set of enumerable properties. If you don't reuse that part, then you're basically just "reusing" property gets, which is a lot of indirection to just get a one-liner. (Literally the only commonality is step 26.4.1.1; note that 26.4.1's predicate is only necessary because of the unbounded enumeration.) |
If we did want consistency with deep though, then it would argue for my
from the OP. |
Ping @yutakahirano @evilpie for thoughts. Summary of the open question: The PR as-drafted contains reasonable semantics, but we could make things more reasonable if we also revisited how |
source
Outdated
<li><p>If <var>name</var> is not one of "Error", "EvalError", "RangeError", "ReferenceError", | ||
"SyntaxError", "TypeError", or "URIError", then set <var>name</var> to "Error".</p></li> | ||
<li><p>If <var>name</var> is not one of "AggregateError", "Error", "EvalError", "RangeError", | ||
"ReferenceError", "SyntaxError", "TypeError", or "URIError", then set <var>name</var> to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sentence seems incomplete.
The last discussion was: #4665 (comment) #4665 (comment). I think the argument is still valid. I can implement this in either way, I believe. |
I am not currently looking into implementing structured cloning for native errors. However if we are going to implement it, it would be better to have |
I got a bit discouraged by the lack of clear answer, and also the fact that the tests for error cloning are annoying to update. I am willing to try again on the spec front if others do the tests and we can ensure implementation interest. It sounds like there's at least support from yourself and @yutakahirano for doing something here in the spec, even if the implementation priority is not very high. As for what the spec should say: at this point I am convinced it should be more generic so that it can adapt to future such error extensions from TC39. I am not sure exactly how, but probably some recursive cloning of object properties (preserving their enumerable/writable/configurable attributes??) would be best. I'll try to write something up and see how I like it... stay tuned. |
* Generalize the framework to work on any NativeError types introduced by the JS spec, instead of listing them explicitly in a way that could require future updates. * Also include WebAssembly Error classes. * Switch semantics for serializing/deserializing "message". Previously, we would check for the presence of the property, and if it was a data property, get its value, and then ToString() it. Now, we just Get() it and then structured-serialize the result. We also unconditionally install it on the other side, regardless of whether it was present on the original. This new property-cloning procedure is more general. * Use these new more general property cloning procedure on "cause" (for all errors) and "errors" (for objects that present as "AggregateError" objects). Closes #5716.
de9bc6e
to
5da5838
Compare
OK, I rewrote it to be reasonably general, although new properties will still need to get added one by one. I'll copy and paste the new commit description here and also into the OP:
Somewhat related: tc39/proposal-error-cause#35 This will require a lot of test updates, which I'm still not signing up for. I kind of wish the TC39 process did not get to go to stage 3 until we had this host integration figured out, or stage 4 until we had tests written; I'm talking with @syg about whether moving some of this responsibility into the ES spec might help with that. |
At least for SpiderMonkey it would be nicer if |
? <span>ToString</span>(<var>valueMessageDesc</var>.[[Value]]) otherwise.</p></li> | ||
<li><p>If <var>name</var> is "AggregateError", then set <var>errors</var> to ? | ||
<span>StructuredSerializeInternal</span>(? <span data-x="js-Get">Get</span>(<var>value</var>, | ||
"errors"), <var>forStorage</var>, <var>memory</var>).</p></li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's better to handle it after caching copy in the memory
for avoiding problems with circular dependencies?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const e = AggregateError([]);
e.errors.push(e);
structuredClone(e);
<li><p>Perform ! <span>CreateNonEnumerableDataPropertyOrThrow</span>(<var>value</var>, "cause", | ||
<var>cause</var>).</p></li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's better to install .cause
only when it's present on the source? Error
constructors do not install it if it's not present in options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an important part of the spec for error causes; a cause of undefined is quite different from an absent cause.
Hey, maybe makes sense to finish it before exposing |
We'd welcome your help in working on a spec that can get implementer interest, and/or doing such implementation work! |
@domenic this PR is already not so bad excepting some mentioned above minor moments. |
Fwiw. We landed serialization support for |
|
||
<li><p>Let <var>message</var> be ? <span>StructuredSerializeInternal</span>(? <span | ||
data-x="js-Get">Get</span>(<var>value</var>, "message"), <var>forStorage</var>, | ||
<var>memory</var>).</p></li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH I'm not sure how much this message
behavior change is useful here, I'd be slightly happier if this is split to a separate PR/discussion.
Given that we lose random property e.g. err.foo
, I think it's not too inconsistent to lose some information here (as both are set outside of the constructor in this case).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you suggesting we change what we do for objects containing [[ErrorData]] today?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really, just saying we don't keep all manually-set properties too hard. Right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should be consistent across error objects on copying message
. And we copy other fields such as stack
too, so that doesn't seem like something we should be changing here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't toString()
behavior what the existing spec says and browsers do currently?
And we copy other fields such as
stack
too,
Hmm, quickly checked and it seems Gecko somehow decided not to copy the stack
property but to copy the internal stack data. @evilpie, do you remember the reasoning behind that decision?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to copy the internal stack so that it works properly with all other consumers in Gecko, which don't want to reparse the stack from a string. It's also required for hiding cross-origin stack frames in content and still having them visible for more privileged code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see now about message
, my bad. I'm not sure I care strongly, as long as it's copied somehow.
I think copying the internal stack data is correct. We tend to prefer serializing internal slots over public data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, and I just noticed that the PR does not use Get()
for stack
but only for cause
and message
, which sounds correct 👍 (with the corresponding WPT here for cross origin case.)
|
||
<li><p>Let <var>cause</var> be ? <span>StructuredSerializeInternal</span>(? <span | ||
data-x="js-Get">Get</span>(<var>value</var>, "cause"), <var>forStorage</var>, | ||
<var>memory</var>).</p></li> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cause
should be conditionally installed as noted below. Firefox adds "hasCause" for this purpose, maybe the same can be done here.
(But it's never been crystal clear to me how it's supposed to be different to not have the property and to have undefined
here, maybe some relevant content in MDN would be helpful.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's different because you can throw undefined
, and then undefined
might be the cause, and that's different from an error with no cause.
Closes #5716.
(See WHATWG Working Mode: Changes for more details.)
Suggested tests for the semantics as specced here:
WebAssembly.CompileError
,WebAssembly.LinkError
, andWebAssembly.RuntimeError
.message
andcause
(updating message tests as appropriate since we're changing the semantics):errors
too, but only onAggregateError
errors
specific tests:errors
property on a non-AggregateError
is ignored, except:AggregateError
to have.name === "AggregateError"
makeserrors
get cloned (and the other side getting anAggregateError
)/infrastructure.html ( diff )
/references.html ( diff )
/structured-data.html ( diff )