If the value we get after fmod in Value::to_u32 is negative, UBSAN
complains that -N is out of bounds for u32. An extra static cast to i64
makes it stop complaining. An alternative implementation could add 2^32
if the fmod'd value is negative. Caught by UBSAN and oss-fuzz.
This was a standalone function previously (get_method()), but instead of
passing a Value to it, we can just make it a method.
Also add spec step comments and fix the receiver value by using GetV().
Like Get(), but with any value instead of an object - it's calling
ToObject() for us and passes the value to [[Get]]() as the receiver.
This will be used in GetMethod() (and a couple of other places, which
can be updated over time).
I also tried something new here: adding the three steps from the spec as
inline comments :^)
Requires a bunch of find-and-replace updates across LibJS, but
constructing a PropertyName from a nullptr Symbol* should not be
possible - let's enforce this at the compiler level instead of using
VERIFY() (and already dereference Symbol pointers at the call site).
Value.{cpp,h} has become a dumping ground, let's change that.
Things that are directly related to Values (e.g. bitwise/binary ops,
equality related functions) can remain, but everything else that's not a
Value or Object method and globally required (not just a static function
somewhere) is being moved.
Also convert to east-const while we're here.
I haven't touched IteratorOperations.{cpp,h}, it seems fine to still
have those separately.
As mentioned on Discord earlier, we'll add these to all new functions
going forward - this is the backfill. Reasons:
- It makes you look at the spec, implementing based on MDN or V8
behavior is a no-go
- It makes finding the various functions that are non-compliant easier,
in the future everything should either have such a comment or, if it's
not from the spec at all, a comment explaining why that is the case
- It makes it easier to check whether a certain abstract operation is
implemented in LibJS, not all of them use the same name as the spec.
E.g. RejectPromise() is Promise::reject()
- It makes it easier to reason about vm.arguments(), e.g. when the
function has a rest parameter
- It makes it easier to see whether a certain function is from a
proposal or Annex B
Also:
- Add arguments to all functions and abstract operations that already
had a comment
- Fix some outdated section numbers
- Replace some ecma-international.org URLs with tc39.es
The second argument (the default constructor) and the return value have
to be constructors (as a result functions), so we can require that
explicitly by using appropriate types.
We already have two separate implementations of this, so let's do it
properly. The optional value type check is done by a callback function
that returns Result<void, ErrorType> - value type accepted or message
for TypeError, that is.
This was missing from Value::is_array(), which is equivalent to the
spec's IsArray() abstract operation - it treats a Proxy value with an
Array target object as being an Array.
It can throw, so needs both the global object and an exception check
now.
Throws an exception if the given value is nullish, returns it otherwise.
We can now gradually replace such manual checks with this function where
applicable.
This also has the advantage that the somewhat useless "ToObject on null
or undefined" will be replaced with "null cannot be converted to an
object" or "undefined cannot be converted to an object". :^)
Problem:
- `static` variables consume memory and sometimes are less
optimizable.
- `static const` variables can be `constexpr`, usually.
- `static` function-local variables require an initialization check
every time the function is run.
Solution:
- If a global `static` variable is only used in a single function then
move it into the function and make it non-`static` and `constexpr`.
- Make all global `static` variables `constexpr` instead of `const`.
- Change function-local `static const[expr]` variables to be just
`constexpr`.
SPDX License Identifiers are a more compact / standardized
way of representing file license information.
See: https://spdx.dev/resources/use/#identifiers
This was done with the `ambr` search and replace tool.
ambr --no-parent-ignore --key-from-file --rep-from-file key.txt rep.txt *
We should be able to get the 'typeof' string for any value directly, so
this is now a standalone Value::typeof() method instead of being part of
UnaryExpression::execute().
Almost a year after first working on this, it's finally done: an
implementation of Promises for LibJS! :^)
The core functionality is working and closely following the spec [1].
I mostly took the pseudo code and transformed it into C++ - if you read
and understand it, you will know how the spec implements Promises; and
if you read the spec first, the code will look very familiar.
Implemented functions are:
- Promise() constructor
- Promise.prototype.then()
- Promise.prototype.catch()
- Promise.prototype.finally()
- Promise.resolve()
- Promise.reject()
For the tests I added a new function to test-js's global object,
runQueuedPromiseJobs(), which calls vm.run_queued_promise_jobs().
By design, queued jobs normally only run after the script was fully
executed, making it improssible to test handlers in individual test()
calls by default [2].
Subsequent commits include integrations into LibWeb and js(1) -
pretty-printing, running queued promise jobs when necessary.
This has an unusual amount of dbgln() statements, all hidden behind the
PROMISE_DEBUG flag - I'm leaving them in for now as they've been very
useful while debugging this, things can get quite complex with so many
asynchronously executed functions.
I've not extensively explored use of these APIs for promise-based
functionality in LibWeb (fetch(), Notification.requestPermission()
etc.), but we'll get there in due time.
[1]: https://tc39.es/ecma262/#sec-promise-objects
[2]: https://tc39.es/ecma262/#sec-jobs-and-job-queues
This commit replaces the usage of String::starts_with with
Utf8View::starts_with, which first decodes the utf8 encoded
string, and as such can take things like overlong encoded
sequences into account (which could otherwise cause the prefix
check to be inconsistent with the following code points check).
Just like to_size_t() - which was already removed in f369229 - this is
non-standard, use to_length() instead. One remaining use was removed,
and I'm glad it's gone. :^)
We now store 32-bit integers as 32-bit integers directly which avoids
having to convert them from doubles when they're only used as 32-bit
integers anyway. :^)
This patch feels a bit incomplete and there's a lot of opportunities
to take advantage of this information. We'll have to find and exploit
them eventually.
(...and ASSERT_NOT_REACHED => VERIFY_NOT_REACHED)
Since all of these checks are done in release builds as well,
let's rename them to VERIFY to prevent confusion, as everyone is
used to assertions being compiled out in release.
We can introduce a new ASSERT macro that is specifically for debug
checks, but I'm doing this wholesale conversion first since we've
accumulated thousands of these already, and it's not immediately
obvious which ones are suitable for ASSERT.