Skip to content
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

Implement Response.clone (#125) #178

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

zerotri
Copy link

@zerotri zerotri commented Nov 27, 2024

This simple commit takes the existing Request.clone implementation and adapts it to work for Response.clone. This was done largely as a learning exercise.

I did have a question regarding the current Response class. It appears that a URL slot is missing from Response where it appears in Request. The spec shows a url attribute which would imply that this slot should be implemented.

Is this intentionally left out or something that just hasn't been added yet?

I have yet to get the WPT tests running properly on my machine, so this has not been tested against those. As such, I am leaving this as a draft until I can validate against those.

* Implement `Response.clone`

This is largely based on the existing `Request.clone` implementation

* Add tests for `Response` to match `Request` tests
@guybedford
Copy link
Contributor

This is amazing, thank you for sharing. If you are able to get the tests running it would be great to get the WPT coverage working here.

In order to get the test coverage updated, we run the tests with WPT_FLAGS="--update-expectations". It does seem like some WPT cases have switched from passing to failing in the process as well - it seems like this is actually as a side effect of adding the feature rather than being an explicit regression, so assuming that is the case that should be okay, but it would be worth confirming this first.

With regards to your question, RequestOrResponse is the base class for both which has a URL slot, then the extended slots for Response pick up at this RequestOrResponse::Slots::count so URL is very much still on Response, implemented via its accessor (https://github.com/bytecodealliance/StarlingMonkey/blob/main/builtins/web/fetch/request-response.cpp#L2021).

Let me know if I can assist further at all with getting the WPT setup working for you.

Copy link
Member

@tschneidereit tschneidereit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really really solid, thank you so much! ❤️

Assuming you get the tests situation sorted out and everything looks good there, I think this is ready to go.

Comment on lines 2309 to 2328
RootedValue cloned_headers_val(cx, JS::NullValue());
RootedObject headers(cx, RequestOrResponse::maybe_headers(self));
if (headers) {
RootedValue headers_val(cx, ObjectValue(*headers));
JSObject *cloned_headers = Headers::create(cx, headers_val, Headers::guard(headers));
if (!cloned_headers) {
return false;
}
cloned_headers_val.set(ObjectValue(*cloned_headers));
} else if (RequestOrResponse::maybe_handle(self)) {
auto handle = RequestOrResponse::headers_handle_clone(cx, self);
JSObject *cloned_headers =
Headers::create(cx, handle.release(),
RequestOrResponse::is_incoming(self) ? Headers::HeadersGuard::Immutable
: Headers::HeadersGuard::Response);
if (!cloned_headers) {
return false;
}
cloned_headers_val.set(ObjectValue(*cloned_headers));
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should be able to use RequestOrResponse::headers_handle_clone for this, reducing complexity a bunch. If that's not quite correct to use here, perhaps it can instead be tweaked?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was unable to work out a simple way to use RequestOrResponse::headers_handle_clone for this, however the solution I've added in a recent commit that constructs a RootedObject based on RequestOrResponse::headers appears to do the trick. I'd love an extra set of eyes on that approach though as I'm still wrapping my head around internal object apis.

RootedObject cloned_headers(cx, RequestOrResponse::headers(cx, self));
if (!cloned_headers) {
  return false;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this isn't quite right: it will always return an instance of builtins::web::fetch::Headers tied to self, not a clone of that each time it's called. You should be able to see this if you create a response, clone it using Response.clone, and then do something like response.headers.set("foo", "bar"): this should now show up on both responses.

Can you say what didn't work when using headers_handle_clone?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave it another look over and was able to get headers_handle_clone to work for this. I'll chalk it up to my own ignorance regarding the codebase. I've mirrored the changes to Request::clone as well and wrote up some changes to the tests to check that the headers are properly cloned rather than shared.

Once any further changes are worked through, do you prefer keeping the individual commits in this PR or would a force-push of squashed commits be preferred?

Comment on lines 2335 to 2337
// ENGINE->dump_value(status_val, stderr);
// ENGINE->dump_value(status_message_val, stderr);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this?

SetReservedSlot(new_response, static_cast<uint32_t>(Slots::Status), status_val);
SetReservedSlot(new_response, static_cast<uint32_t>(Slots::StatusMessage), status_message_val);

// 3. If response’s body is non-null, then set newResponse’s body to the result of cloning response’s body.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 3. If response’s body is non-null, then set newResponse’s body to the result of cloning response’s body.
// 3. If response’s body is non-null, then set newResponse’s body to the result of cloning response’s body.

Accessing can of course be done via the `url` accessor in the base class, this is just here for consistency's sake.
* greatly simplifies the cloning code to rely on `RequestOrResponse::headers`, which appears to do a lot of the necessary work for us.

* Also modified `Request.clone` to be consistent with this change.

* `Response.clone` now also copies the `URL` slot
@zerotri
Copy link
Author

zerotri commented Dec 1, 2024

The latest commits expose the URL slot to make Response consistent with Request, as well as make sure to copy the value as part of Response.clone. In addition, the code for cloning Response is greatly simplified, so long as constructing a RootedObject from RequestOrResponse::headers actually works as I am hoping it does. Testing the before and after does appear to work as expected.

I was able to get WPT running on my machine and it is reporting:

1: Running test fetch/api/response/response-clone.any.js
1: Sending request to http://127.0.0.1:7676/fetch/api/response/response-clone.any.js
1: wasmtime stderr: stderr [83] :: Warning: Using a DEBUG build. Expect things to be SLOW.
1: wasmtime stdout: stdout [83] :: Log: running test /fetch/api/response/response-clone.any.js
1: fetch/api/response/response-clone.any.js                           6 /   21 (   +0,    -0,    ?0) passing in    0ms

This would imply that some amount of tests are failing, so I will dig into these further to see if I can aim for higher success rate in WPT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants