Commit graph

40 commits

Author SHA1 Message Date
Timothy Flynn
4d10911f96 LibJS: Pre-allocate the out-of-memory error string on the VM
If we are out of memory, we can't try to allocate a string that could
fail as well. When Error is converted to String, this would result in an
endless OOM-throwing loop. Instead, pre-allocate the string on the VM,
and use it to construct the Error.

Note that as of this commit, the OOM string is still a DeprecatedString.
This is just preporatory for Error's conversion to String.
2023-02-17 09:14:23 -05:00
Timothy Flynn
8f9659a549 LibJS: Surround the VM parameter of TRY_OR_THROW_OOM with parentheses
Depending on how this is invoked, the preprocessor may get confused when
pasting the VM parameter into this expression. For example, it trips up
on the JS REPL in cases such as:

    TRY_OR_THROW_OOM(*g_vm, ...);
2023-02-10 17:26:20 +00:00
Timothy Flynn
604d5f5bca AK+Everywhere: Do not implicitly copy variables in TRY macros
For example, consider cases where we want to propagate errors only in
specific instances:

    auto result = read_data(); // something like ErrorOr<ByteBuffer>
    if (result.is_error() && result.error().code() != EINTR)
        continue;
    auto bytes = TRY(result);

The TRY invocation will currently copy the byte buffer when the
expression (in this case, just a local variable) is stored into
_temporary_result.

This patch binds the expression to a reference to prevent such copies.
In less trival invocations (such as TRY(some_function()), this will
incur only temporary lifetime extensions, i.e. no functional change.
2023-02-10 09:08:52 +00:00
Timothy Flynn
109b190a19 LibJS: Fully qualify the use of AK::is in MUST_OR_THROW_OOM
This is to allow using this macro in contexts that have defined `is`
already. For example, in ObjectConstructor, there is a native function
`is` which would trip up the compiler without this change.
2023-01-29 00:02:45 +00:00
Timothy Flynn
2b5054c903 LibJS: Add a method to ThrowCompletionOr to drop allocation errors
This should solely be used to ignore completions from Heap::allocate in
currently-infallible contexts. It's mostly meant to let us both ignore
these errors and mark them with a FIXME in one go.
2023-01-29 00:02:45 +00:00
Linus Groh
0f3899b24a LibJS: Add spec comments to Completion 2023-01-28 00:41:18 +00:00
Timothy Flynn
52b76060f9 LibJS: Add a macro for infallible operations that may throw OOM
If a spec step hasn't been marked as fallible, but might throw due to
OOM, this is to make it clear that OOM is the only thing that may cause
a failure.
2023-01-20 20:31:38 +00:00
Timothy Flynn
5349972f41 LibJS: Add a special ThrowCompletionOr constructor for OptionalNone
Currently, if you have a fallible function with an Optional return type
and try to return OptionalNone like this:

    ThrowCompletionOr<Optional<SomeType>> { return OptionalNone {}; }

Then ThrowCompletionOr's m_value (whose type is an Optional<ValueType>)
will be set to empty, rather than containing an empty Optional. This is
due to the ThrowCompletionOr's WrappedValueType constructor.

This patch adds a constructor specifically for OptionalNone. If someone
attempts to construct a ThrowCompletionOr with OptionalNone, but the
ValueType is not an Optional, a compile error like the following will
occur:

    ThrowCompletionOr<String> foo() { return OptionalNone {}; }

    error: no matching constructor for initialization of 'AK::String'

Otherwise, it will fill ThrowCompletionOr's m_value with an empty
Optional.
2023-01-19 23:13:44 +00:00
Andrew Kaster
f5d253dcfa Everywhere: Fully qualify IsLvalueReference in TRY() macros
If USING_AK_GLOBALLY is not defined, the name IsLvalueReference might
not be available in the global namespace. Follow the pattern established
in LibTest to fully qualify AK types in macros to avoid this problem.
2023-01-15 00:56:31 +00:00
Timothy Flynn
afc0e461e1 AK+Everywhere: Disallow returning a reference from a fallible expression
This will silently make a copy. Rather than masking this behavior, let's
explicitly disallow it.
2023-01-13 18:50:47 -05:00
Timothy Flynn
3de75f6436 LibJS: Explicitly disallow references in ThrowCompletionOr
This will be disallowed by TRY soon, but the compile error produced by
this requirement will pinpoint the errant line more accurately.
2023-01-13 18:50:47 -05:00
Timothy Flynn
f3db548a3d AK+Everywhere: Rename FlyString to DeprecatedFlyString
DeprecatedFlyString relies heavily on DeprecatedString's StringImpl, so
let's rename it to A) match the name of DeprecatedString, B) write a new
FlyString class that is tied to String.
2023-01-09 23:00:24 +00:00
Timothy Flynn
d8044c5358 LibJS+LibWeb: Move the macro to convert ENOMEM to an exception to LibJS
Move the macro to LibJS and change it to return a throw completion
instead of a WebIDL exception. This will let us use this macro within
LibJS to handle OOM conditions.
2023-01-08 12:13:15 +01:00
Timothy Flynn
ba97f6a0d3 LibJS: Move the "other" optional completion in the move constructor
Otherwise, we invoke a non-trival copy. Caught by clangd.
2023-01-08 12:13:15 +01:00
Linus Groh
029db614e3 LibJS: Ensure Optional<Completion>'s defaults to empty completion
Default-constructing the m_value Completion made it have an undefined
JS value when not overridden in a constructor, such as the conditional
initialization in Optional(Optional<JS::Completion> const&).

See investigation by Tim here:
https://github.com/SerenityOS/serenity/pull/16498#discussion_r1049090456

Co-authored-by: Timothy Flynn <trflynn89@pm.me>
2022-12-15 06:56:37 -05:00
Andreas Kling
42b5c896e8 LibJS: Don't "copy construct" temporary value in ThrowCompletionOr ctor
It was possible for the generic ThrowCompletionOr constructor to
"copy-construct" a JS Object when instantiating a ThrowCompletionOr
via e.g `return *object;`.

This happened because it chose the Object(Object& prototype) constructor
which will be removed in a subsequent commit. It was not easy to debug.

As a first step towards avoiding this in the future, the generic
ThrowCompletionOr constructor now takes the value as a const reference.
2022-12-14 15:11:57 +01:00
Linus Groh
d26aabff04 Everywhere: Run clang-format 2022-12-03 23:52:23 +00:00
Andreas Kling
88b15b0819 LibJS: Move throw_completion(Value) out of line
This allows us to instrument this function locally without rebuilding
1000+ files.
2022-11-09 15:48:08 +01:00
Linus Groh
56b2ae5ac0 LibJS: Replace GlobalObject with VM in remaining AOs [Part 19/19] 2022-08-23 13:58:30 +01:00
davidot
6c504e2bff LibJS: Specialize Optional<Completion>
Since Completion has an enum for state we have plenty of space to add
an extra type which indicates on empty completion.
Since Optional<Completion> is used in every ThrowCompletionOr<...>
this saves quite some stack space as most function in LibJS return
types like that.
This saves 8 bytes for every Optional<Completion>.
2022-08-15 17:11:25 +02:00
Linus Groh
9f3f3b0864 LibJS: Remove implicit wrapping/unwrapping of completion records
This is an editorial change in the ECMA-262 spec, with similar changes
in some proposals.

See:
- https://github.com/tc39/ecma262/commit/7575f74
- https://github.com/tc39/proposal-array-grouping/commit/df899eb
- https://github.com/tc39/proposal-shadowrealm/commit/9eb5a12
- https://github.com/tc39/proposal-shadowrealm/commit/c81f527
2022-05-03 01:09:29 +02:00
Linus Groh
c9bdd59e20 LibJS: Change "Completion {}" to "Completion Record {}" in comments
This is an editorial change in the ECMA-262 spec.

See: https://github.com/tc39/ecma262/commit/15a7d8a
2022-05-01 22:47:38 +02:00
Hendiadyoin1
c79e4961f6 LibJS: Add explicit default copy+move constructors to ThrowCompletionOr
This stops clangd from complaining about not being able to determine the
copy-constructibility of ThrowCompletionOr and Completion.
2022-03-31 09:25:17 -04:00
Timothy Flynn
a78058bc79 LibJS: Do not refer to moved-from completions / values
In the ThrowCompletionOr constructors, the VERIFY statements are using
moved-from objects. We should not rely on those objects still being
valid after being moved.
2022-02-10 14:10:34 +00:00
Andreas Kling
4d785b9aa0 LibJS: Mark Completion constructors with ALWAYS_INLINE
These were showing up in profiles.
2022-02-06 16:30:57 +01:00
Linus Groh
bfa42cbe4f LibJS: Remove the now retired TRY_OR_DISCARD() macro :^) 2022-01-04 23:37:26 +00:00
Linus Groh
95acb1ce88 LibJS: Don't assume non-empty [[Value]] in Completion TRY() helpers 2022-01-03 21:50:50 +01:00
Linus Groh
85f0fc2b83 LibJS: Return Optional<T> from Completion::{value,target}(), not T
In the end this is a nicer API than having separate has_{value,target}()
and having to check those first, and then making another Optional from
the unwrapped value:

    completion.has_value() ? completion.value() : Optional<Value> {}
    //                       ^^^^^^^^^^^^^^^^^^
    //         Implicit creation of non-empty Optional<Value>

This way we need to unwrap the optional ourselves, but can easily pass
it to something else as well.

This is in anticipation of the AST using completions :^)
2022-01-03 21:50:50 +01:00
Linus Groh
7565b1fda8 LibJS: Let Completion::update_empty() take an Optional<Value>
It also needs to be able to take what the spec calls 'empty', which is
an Optional<Value> in this case (*not* an empty JS::Value). The common
use case is updating a completion with another completion, that may also
have an empty [[Value]] slot.
2022-01-02 20:48:38 +01:00
Linus Groh
13777d4886 LibJS: Move provided Optional<Value> argument in normal_completion() 2022-01-02 20:47:14 +01:00
Linus Groh
68ac13a192 LibJS: Add a Completion(ThrowCompletionOr<Value> const&) constructor 2021-11-14 15:27:46 +00:00
Andreas Kling
cd49f30bea AK+LibJS: Simplify MUST() and move it from LibJS to AK/Try.h
This is generally useful so let's move it to AK. Also it seems that we
don't need the temporary variable hack anymore, so let's lose that.
2021-11-10 21:58:58 +01:00
Idan Horowitz
46dabf02ec LibJS: Add support for await expressions 2021-11-10 08:48:27 +00:00
Linus Groh
894834b5d5 LibJS: Allow construction of ThrowCompletionOr<Value> from non-Value
TL;DR: Instead of this:

    return { TRY(my_object()) };

we can now do:

    return TRY(my_object());

just like we mostly did for Value return types before ThrowCompletionOr.
2021-10-21 09:02:23 +01:00
Linus Groh
1b1bf5c321 LibJS: Change normal_completion() parameter to Optional<Value>
The Completion constructor `VERIFY()`s that the passed argument is not
an empty Value, so normal_completion({}) would crash (although it's
currently not being used anywhere).
We want to pass an empty Optional<Value> instead.
2021-10-14 11:13:05 +01:00
Linus Groh
7fc2807929 LibJS: Add Completion::is_abrupt()
This is commonly used in the spec.
2021-10-09 14:29:20 +01:00
Linus Groh
7cd3f7de61 LibJS: Add a MUST() macro, like TRY() but for the spec's ! shortcut 2021-10-03 20:14:03 +01:00
Linus Groh
6bdd62b51b LibJS: Convert Now AOs to ThrowCompletionOr 2021-09-17 23:43:01 +02:00
Linus Groh
fea27143e9 LibJS: Initialize value in ThrowCompletionOr<void> default constructor
Otherwise, TRY() will crash when calling release_value() on the empty
m_value Optional.
2021-09-16 22:34:24 +01:00
Linus Groh
33679a8445 LibJS: Add a JS::Completion class and JS::ThrowCompletionOr<T> template
We decided that we want to move away from throwing exceptions in AOs
and regular functions implicitly and then returning some
default-constructed value (usually an empty JS::Value) - this requires
remembering to check for an exception at the call site, which is
error-prone. It's also awkward for return values that cannot be
default-constructed, e.g. MarkedValueList.
Instead, the thrown value should be part of the function return value.

The solution to this is moving closer to the spec and using something
they call "completion records":
https://tc39.es/ecma262/#sec-completion-record-specification-type

This has various advantages:

- It becomes crystal clear whether some AO can fail or not, and errors
  need to be handled and values unwrapped explicitly (for that reason
  compatibility with the TRY() macro is already built-in, and a similar
  TRY_OR_DISCARD() macro has been added specifically for use in LibJS,
  while the majority of functions doesn't return ThrowCompletionOr yet)
- We no longer need to mix "valid" and "invalid" values of various types
  for the success and exception outcomes (e.g. null/non-null AK::String,
  empty/non-empty JS::Value)
- Subsequently it's no longer possible to accidentally use an exception
  outcome return value as a success outcome return value (e.g. any AO
  that returns a numeric type would return 0 even after throwing an
  exception, at least before we started making use of Optional for that)
- Eventually the VM will no longer need to store an exception, and
  temporarily clearing an exception (e.g. to call a function) becomes
  obsolete - instead, completions will simply propagate up to the caller
  outside of LibJS, which then can deal with it in any way
- Similar to throw we'll be able to implement the functionality of
  break, continue, and return using completions, which will lead to
  easier to understand code and fewer workarounds - the current
  unwinding mechanism is not even remotely similar to the spec's
  approach

The spec's NormalCompletion and ThrowCompletion AOs have been
implemented as simple wrappers around the JS::Completion constructor.
UpdateEmpty has been implemented as a JS::Completion method.

There's also a new VM::throw_completion<T>() helper, which basically
works like VM::throw_exception<T>() - it creates a T object (usually a
JS::Error), and returns it wrapped in a JS::Completion of Type::Throw.

Two temporary usage patterns have emerged:

1. Callee already returns ThrowCompletionOr, but caller doesn't:

    auto foo = TRY_OR_DISCARD(bar());

2. Caller already returns ThrowCompletionOr, but callee doesn't:

    auto foo = bar();
    if (auto* exception = vm.exception())
        return throw_completion(exception->value());

Eventually all error handling and unwrapping can be done with just TRY()
or possibly even operator? in the future :^)

Co-authored-by: Andreas Kling <kling@serenityos.org>
2021-09-15 23:46:53 +01:00