-
Notifications
You must be signed in to change notification settings - Fork 19
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
Add File interface #181
base: main
Are you sure you want to change the base?
Add File interface #181
Conversation
94de20c
to
72b5f33
Compare
72b5f33
to
5cdc538
Compare
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.
While I left a few inline comments on the code as written, after getting far enough in the review I realized that I find it very difficult to reason through how the code implements the specification.
For example, when trying to understand the File
constructor, I'm not sure how it implements the log of (simplifying here!) iterating through the list of fileBits
and appending them to the blob underlying the File
. If I understand the code correctly, it has a special case for a sequence whose first entry is a File
instance. If that case is hit, it copies that File
's contents and ignores the rest of the sequence?
But I might misunderstand the code here—that's very plausible!
Which is why I'd like to ask for something that's unfortunately a bunch of additional work: could you go through the code and add comments for which parts of the spec are implemented where? You can see examples of that in the various other builtins, in particular the ones for the fetch
API. One that I think is quite decent is Response::redirect.
I used to have a script to turn the spec's source into C++ comments, but unfortunately seem to have lost it :( In the meantime, perhaps the easiest way to quickly get decent comments to use is roughly this:
- find the spec algorithm you want to implement/get comments for
- select the algorithms text in your browser
- select "view source" (this works well at least in Firefox, I haven't tried other browsers)
- copy the source
- paste it into an html-to-markdown converter, such as https://htmlmarkdown.com/
- prefix all lines in the output with
//
That should give you something that's at least not too far away from usable.
As I said, I know that I'm asking for a whole bunch of extra work here, and I'm sorry for that. I don't really know of a better way to ensure that we're actually implementing the spec as written: the WPT suite gives us decent assurances, but it's not complete and doesn't cover all corner cases.
Hey @tschneidereit ! Thanks for the feedback! I can definitely add comments with the steps from the spec - that’s a good point. I've been thinking about this implementation, and I feel like part of the issue here is that I don’t have a clear understanding of how to model inheritance using I noticed that the We shouldn’t need to add a special case for something like BTW, I don't mind redoing this PR from scratch as long as we can come up with better and more idiomatic code :) |
Hey @tschneidereit I refactored the code so that I will prepare a separate PR for |
builtins/web/blob.h
Outdated
@@ -51,13 +51,19 @@ class Blob : public TraceableBuiltinImpl<Blob> { | |||
static const JSPropertySpec properties[]; | |||
|
|||
static constexpr unsigned ctor_length = 0; | |||
enum Slots { Data, Type, Endings, Readers, Count }; | |||
enum Slots { Data, Type, Endings, Readers, Reserved1, Reserved2, Count }; |
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.
Since these are being reserved by the FIle API, it may just make sense to directly call these MaybeFileName
and MaybeFileModified
, since those are their only meanings. Alternatively explicitly note they are reserved by the File API - FileAPISlot1
and FileAPISlot2
.
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.
Also I'm curious why not just have a File
slot and have it point to a file instance here, where the File builtin has the Name
and Modified
slots?
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.
My original goal was to use construct
JS::Construct(cx, blob_ctor_val, file_ctor_val, blob_args, &self);
so that internally it behaved like:
Reflect.construct(Blob, blobArgs, File);
and automatically allocated an object with the File class (allowing to set up extra slots such as name
and lastModified
). But for some reason JS_NewObjectForConstructor
in the Blob
constructor always produced a Blob object regardless of the newTarget
.
I now switched to a simpler manual approach:
- Create a File-class object directly,
- Call Blob’s initialization routine on it (for shared functionality),
- Then add the File-specific slots in the File constructor.
This keeps the “subclass” logic clean, avoids forcing Blob to know about File, and ensures we get a proper File object with all its extra slots.
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.
Interestingly it also looks like using subclassing is now discouraged:
Warning: The standard committee now holds the position that the built-in subclassing mechanism in previous spec versions is over-engineered and causes non-negligible performance and security impacts. New built-in methods consider less about subclasses, and engine implementers are investigating whether to remove certain subclassing mechanisms. Consider using composition instead of inheritance when enhancing built-ins.
builtins/web/file.cpp
Outdated
auto now = std::chrono::system_clock::now(); | ||
auto ms_since_epoch = | ||
std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count(); |
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 should use the hostAPI wrapper function for these - MonotonicClock::now
.
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've added the WallClock
simple wrapper. The File implementation expects the timestamp to be the time in ms since Unix Epoch. This is what wasi_wall_clock
API provides.
Should we also revisit the Performance
implementation? It looks like it is using std::chrono
.
|
||
bool File::name_get(JSContext *cx, unsigned argc, JS::Value *vp) { | ||
METHOD_HEADER(0); | ||
// TODO: Change this class so that its prototype isn't an instance of the class |
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.
Can you clarify what is meant by this? Is the instance prototype not supposed to be an instance of the class, or the File.prototype
constructor "prototype"
property?
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.
Sorry, no idea. I've been carrying this comment from other classes without giving it much thought.
wasi_clocks_wall_clock_datetime_t time_now; | ||
wasi_clocks_wall_clock_now(&time_now); | ||
|
||
uint64_t total_nanoseconds = time_now.seconds * 1000000000ULL + time_now.nanoseconds; |
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 will overflow around year 2554. ¯\_(ツ)_/¯
https://w3c.github.io/FileAPI/#file-section
Closes: #155