Commit graph

139 commits

Author SHA1 Message Date
Linus Groh
09a11fa6ea LibJS: Implement proper Iterator records
Instead of using plain objects as Iterator records, causes confusion
about the object itself actually being its [[Iterator]] slot, and
requires non-standard type conversion shenanigans fpr the [[NextValue]]
and [[Done]] internal slots,  implement a proper Iterator record struct
and use it throughout.

Also annotate the remaining Iterator AOs with spec comments while we're
here.
2022-01-09 22:02:43 +01:00
Linus Groh
963b0f76cf LibJS: Remove now unused VM::{set_,}last_value()
This was effectively replaced by correct use of completions, including
UpdateEmpty semantics.
2022-01-08 23:43:03 +01:00
Linus Groh
9d0d3affd4 LibJS: Replace the custom unwind mechanism with completions :^)
This includes:

- Parsing proper LabelledStatements with try_parse_labelled_statement()
- Removing LabelableStatement
- Implementing the LoopEvaluation semantics via loop_evaluation() in
  each IterationStatement subclass; and IterationStatement evaluation
  via {For,ForIn,ForOf,ForAwaitOf,While,DoWhile}Statement::execute()
- Updating ReturnStatement, BreakStatement and ContinueStatement to
  return the appropriate completion types
- Basically reimplementing TryStatement and SwitchStatement according to
  the spec, using completions
- Honoring result completion types in AsyncBlockStart and
  OrdinaryCallEvaluateBody
- Removing any uses of the VM unwind mechanism - most importantly,
  VM::throw_exception() now exclusively sets an exception and no longer
  triggers any unwinding mechanism.
  However, we already did a good job updating all of LibWeb and userland
  applications to not use it, and the few remaining uses elsewhere don't
  rely on unwinding AFAICT.
2022-01-06 12:36:23 +01:00
Linus Groh
da856d7742 LibJS: Update AST to use completions :^)
This is another major milestone on our journey towards removing global
VM exception state :^)
Does pretty much exactly what it says on the tin: updating
ASTNode::execute() to return a Completion instead of a plain value. This
will *also* allow us to eventually remove the non-standard unwinding
mechanism and purely rely on the various completion types.
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
davidot
c296df6b58 LibJS: Convert to_reference() to ThrowCompletionOr 2021-12-31 00:03:20 +01:00
davidot
a24df37713 LibJS: Convert resolve_this_binding() to ThrowCompletionOr
Also add spec comments.
2021-12-31 00:03:20 +01:00
davidot
b303b8cf4e LibJS: Convert thrown exception to completion in binding initialization
This regressed in 676554d3 as it assumed to_reference() (already)
returned a completion type instead of putting the error in
vm.exception().
2021-12-30 20:14:13 +01:00
davidot
d72022ba04 LibJS: Convert get_identifier_reference() to ThrowCompletionOr
And while we're here add spec comments.
2021-12-30 15:29:33 +01:00
davidot
676554d3f8 LibJS: Convert resolve_binding() to ThrowCompletionOr
The spec has a note stating that resolve binding will always return a
reference whose [[ReferencedName]] field is name. However this is not
correct as the underlying method GetIdentifierReference may throw on
env.HasBinding(name) thus it can throw. However, there are some
scenarios where it cannot throw because the reference is known to exist
in that case we use MUST with a comment.
2021-12-30 15:29:33 +01:00
Linus Groh
df931e6a83 LibJS: Implement and use the InitializeBoundName AO 2021-12-29 10:34:28 +01:00
Linus Groh
ca48151147 LibJS: Add spec comments to VM::binding_initialization() 2021-12-29 10:34:23 +01:00
Linus Groh
451149df0b LibJS: Ensure get_new_target() never returns an empty value
Also add spec comments and remove a redundant exception check while
we're here :^)
2021-12-29 00:16:51 +01:00
Timothy Flynn
d69f5ca128 LibJS: Update spec numbers for Operations on Objects AOs
The error cause proposal was merged, so some spec numbers were bumped.
2021-12-21 14:56:28 +01:00
Andreas Kling
4790f9a628 LibJS: Make sure private environments are marked during GC 2021-12-08 10:29:54 +01:00
Linus Groh
57de5056b6 LibJS: Convert push_execution_context() to ThrowCompletionOr 2021-11-14 16:14:38 +00:00
Linus Groh
a53542e0a3 LibJS: Clear exception after running each queued Promise job
It's not what the spec tells us to do. In fact, the spec tells us the
exact opposite:

    9.5 Jobs and Host Operations to Enqueue Jobs
    https://tc39.es/ecma262/#sec-jobs

    A Job is an Abstract Closure with no parameters that initiates an
    ECMAScript computation when no other ECMAScript computation is
    currently in progress.
    ...
    Their implementations must conform to the following requirements:
    - ...
    - The Abstract Closure must return a normal completion, implementing
      its own handling of errors.

However, this turned out to not be true in all cases. More specifically,
the NewPromiseReactionJob AO returns the completion result of calling a
user-provided function (PromiseCapability's [[Resolve]] / [[Reject]]),
which may be an abrupt completion:

    27.2.2.1 NewPromiseReactionJob ( reaction, argument )
    https://tc39.es/ecma262/#sec-newpromisereactionjob

    1. Let job be a new Job Abstract Closure with no parameters that
       captures reaction and argument and performs the following steps
       when called:
       ...
       h. If handlerResult is an abrupt completion, then
          i. Let status be Call(promiseCapability.[[Reject]],
             undefined, « handlerResult.[[Value]] »).
       i. Else,
          i. Let status be Call(promiseCapability.[[Resolve]],
             undefined, « handlerResult.[[Value]] »).
       j. Return Completion(status).

Interestingly, this case is explicitly handled in the HTML spec's
implementation of jobs as microtasks:

    8.1.5.3.3 HostEnqueuePromiseJob(job, realm)
    https://html.spec.whatwg.org/webappapis.html#hostenqueuepromisejob

    2. Queue a microtask on the surrounding agent's event loop to
       perform the following steps:
       ...
       5. If result is an abrupt completion, then report the exception
          given by result.[[Value]].

This is precisely what all the major engines do - but not only in
browsers; the provided code snippet in the test added in this commit
works just fine in Node.js, for example.

SpiderMonkey:
https://searchfox.org/mozilla-central/rev/25997ce8267ec9e3ea4b727e0973bd9ef02bba79/js/src/builtin/Promise.cpp#6292
https://searchfox.org/mozilla-central/rev/25997ce8267ec9e3ea4b727e0973bd9ef02bba79/js/src/builtin/Promise.cpp#1277
https://searchfox.org/mozilla-central/rev/25997ce8267ec9e3ea4b727e0973bd9ef02bba79/js/src/vm/JSContext.cpp#845

JavaScriptCore:
https://trac.webkit.org/browser/webkit/trunk/Source/JavaScriptCore/builtins/PromiseOperations.js?rev=273718#L562
https://trac.webkit.org/browser/webkit/trunk/Source/JavaScriptCore/runtime/JSMicrotask.cpp?rev=273718#L94

V8:
https://source.chromium.org/chromium/chromium/src/+/main:v8/src/builtins/promise-abstract-operations.tq;l=481;drc=a760f03a6e99bf4863d8d21c5f7896a74a0a39ea
https://source.chromium.org/chromium/chromium/src/+/main:v8/src/builtins/builtins-microtask-queue-gen.cc;l=331;drc=65c9257f1777731d6d0669598f6fe6fe65fa61d3

This should probably be fixed in the ECMAScript spec to relax the rule
that Jobs may not return an abrupt completion, just like in the HTML
spec. The important bit is that those are not surfaced to user code in
any way.
2021-11-12 15:35:03 +02:00
Idan Horowitz
853fab352d LibJS: Convert the InitializeReferencedBinding AO to ThrowCompletionOr 2021-11-02 19:48:35 +01:00
Idan Horowitz
1aaaf521b8 LibJS: Convert the PutValue AO to ThrowCompletionOr 2021-11-02 19:48:35 +01:00
Andreas Kling
7ccb8c8609 LibJS: Provide default hash traits for JS::PropertyKey
Let's not require people to use PropertyNameTraits everywhere when we
can just specialize AK::Traits<JS::PropertyKey> instead. :^)
2021-10-24 17:18:09 +02:00
Andreas Kling
398c181c79 LibJS: Rename PropertyName to PropertyKey
Let's use the same name as the spec. :^)
2021-10-24 17:18:07 +02:00
Idan Horowitz
e26d9f419b LibJS: Remove vm.construct and it's usages 2021-10-23 02:49:41 +03:00
Idan Horowitz
db5df26841 LibJS: Convert Array AOs to ThrowCompletionOr 2021-10-22 15:07:04 +03:00
Timothy Flynn
ec54a7b5b0 LibJS: Implement IteratorClose with Completions and align to the spec 2021-10-21 00:26:45 +01:00
Timothy Flynn
c981d7b9bd LibJS: Convert IteratorNext AO to ThrowCompletionOr 2021-10-21 00:26:45 +01:00
Timothy Flynn
860a37640b LibJS: Convert GetIterator AO to ThrowCompletionOr 2021-10-21 00:26:45 +01:00
davidot
16cc82460f LibJS: Add parsing and evaluation of private fields and methods 2021-10-20 23:19:17 +01:00
Idan Horowitz
c488f5a59d LibJS: Convert to_property_key() to ThrowCompletionOr 2021-10-17 12:12:35 +01:00
Linus Groh
52976bfac6 LibJS: Convert to_object() to ThrowCompletionOr 2021-10-13 09:55:10 +01:00
Andreas Kling
b819719860 LibJS: Make sure queued promise jobs have an execution context when run 2021-10-11 22:21:46 +02:00
Linus Groh
ae397541fb LibJS: Convert initialize_binding() to ThrowCompletionOr
Also add spec step comments to it while we're here.
2021-10-09 21:53:47 +01:00
Linus Groh
fbb176c926 LibJS: Convert has_binding() to ThrowCompletionOr
Also add spec step comments to it while we're here.
2021-10-09 21:53:47 +01:00
Linus Groh
0aa24f4ce5 LibJS: Convert get_this_binding() to ThrowCompletionOr
Also add spec step comments to it while we're here.
2021-10-09 21:53:47 +01:00
Linus Groh
53af66d57d LibJS: Move ordinary_call_bind_this() to ECMAScriptFunctionObject
Now that it only needs to deal with ECMAScriptFunctionObject via
internal_call() / internal_construct(), we can:

- Remove the generic FunctionObject parameter
- Move it from the VM to ECMAScriptFunctionObject
- Make it private
2021-10-09 14:29:20 +01:00
Linus Groh
25bcd36116 LibJS: Move prepare_for_ordinary_call() to ECMAScriptFunctionObject
Now that it only needs to deal with ECMAScriptFunctionObject via
internal_call() / internal_construct(), we can:

- Remove the generic FunctionObject parameter
- Move it from the VM to ECMAScriptFunctionObject
- Make it private
2021-10-09 14:29:20 +01:00
Linus Groh
cf168fac50 LibJS: Implement [[Call]] and [[Construct]] internal slots properly
This patch implements:

- Spec compliant [[Call]] and [[Construct]] internal slots, as virtual
  FunctionObject::internal_{call,construct}(). These effectively replace
  the old virtual FunctionObject::{call,construct}(), but with several
  advantages:
  - Clear and consistent naming, following the object internal methods
  - Use of completions
  - internal_construct() returns an Object, and not Value! This has been
    a source of confusion for a long time, since in the spec there's
    always an Object returned but the Value return type in LibJS meant
    that this could not be fully trusted and something could screw you
    over.
  - Arguments are passed explicitly in form of a MarkedValueList,
    allowing manipulation (BoundFunction). We still put them on the
    execution context as a lot of code depends on it (VM::arguments()),
    but not from the Call() / Construct() AOs anymore, which now allows
    for bypassing them and invoking [[Call]] / [[Construct]] directly.
    Nothing but Call() / Construct() themselves do that at the moment,
    but future additions to ECMA262 or already existing web specs might.
- Spec compliant, standalone Call() and Construct() AOs: currently the
  closest we have is VM::{call,construct}(), but those try to cater to
  all the different function object subclasses at once, resulting in a
  horrible mess and calling AOs with functions they should never be
  called with; most prominently PrepareForOrdinaryCall and
  OrdinaryCallBindThis, which are only for ECMAScriptFunctionObject.

As a result this also contains an implicit optimization: we no longer
need to create a new function environment for NativeFunctions - which,
worth mentioning, is what started this whole crusade in the first place
:^)
2021-10-09 14:29:20 +01:00
Andreas Kling
41a072bded LibJS: Fast non-local variable access :^)
This patch introduces the "environment coordinate" concept, which
encodes the distance from a variable access to the binding it ends up
resolving to.

EnvironmentCoordinate has two fields:

    - hops:  The number of hops up the lexical environment chain we have
             to make before getting to the resolved binding.

    - index: The index of the resolved binding within its declarative
             environment record.

Whenever a variable lookup resolves somewhere inside a declarative
environment, we now cache the coordinates and reuse them in subsequent
lookups. This is achieved via a coordinate cache in JS::Identifier.

Note that non-strict direct eval() breaks this optimization and so it
will not be performed if the resolved environment has been permanently
screwed by eval().

This makes variable access *significantly* faster. :^)
2021-10-07 11:53:18 +02:00
Andreas Kling
4444bcabde LibJS: Make Reference aware of DeclarativeEnvironment indices
VM::resolve_binding() can now return a Reference that knows the exact
binding index if it's pointing into a DeclarativeEnvironment.

Reading/writing through the Reference will now use direct environment
access when possible.
2021-10-07 00:23:36 +02:00
Andreas Kling
0cb4d48283 LibJS: Remove unused ExecutionContext::arguments_object 2021-10-03 23:58:21 +02:00
Linus Groh
b7e5f08e56 LibJS: Convert Object::get() to ThrowCompletionOr
To no one's surprise, this patch is pretty big - this is possibly the
most used AO of all of them. Definitely worth it though.
2021-10-03 20:14:03 +01:00
Andreas Kling
406d3199d0 LibJS: Add a way to save/restore the entire execution context stack
This will be used by LibWeb to squirrel away the stack while performing
a microtask checkpoint in some cases. VM will simply consider saved
execution context stacks as GC roots as well.
2021-10-03 16:42:34 +02:00
davidot
1bc945860d Everywhere: Use my awesome new serenityos email :^) 2021-10-03 13:53:47 +01:00
davidot
830ea0414c LibJS: Make scoping follow the spec
Before this we used an ad-hoc combination of references and 'variables'
stored in a hashmap. This worked in most cases but is not spec like.
Additionally hoisting, dynamically naming functions and scope analysis
was not done properly.

This patch fixes all of that by:
  - Implement BindingInitialization for destructuring assignment.
  - Implementing a new ScopePusher which tracks the lexical and var
    scoped declarations. This hoists functions to the top level if no
    lexical declaration name overlaps. Furthermore we do checking of
    redeclarations in the ScopePusher now requiring less checks all over
    the place.
  - Add methods for parsing the directives and statement lists instead
    of having that code duplicated in multiple places. This allows
    declarations to pushed to the appropriate scope more easily.
  - Remove the non spec way of storing 'variables' in
    DeclarativeEnvironment and make Reference follow the spec instead of
    checking both the bindings and 'variables'.
  - Remove all scoping related things from the Interpreter. And instead
    use environments as specified by the spec. This also includes fixing
    that NativeFunctions did not produce a valid FunctionEnvironment
    which could cause issues with callbacks and eval. All
    FunctionObjects now have a valid NewFunctionEnvironment
    implementation.
  - Remove execute_statements from Interpreter and instead use
    ASTNode::execute everywhere this simplifies AST.cpp as you no longer
    need to worry about which method to call.
  - Make ScopeNodes setup their own environment. This uses four
    different methods specified by the spec
    {Block, Function, Eval, Global}DeclarationInstantiation with the
    annexB extensions.
  - Implement and use NamedEvaluation where specified.

Additionally there are fixes to things exposed by these changes to eval,
{for, for-in, for-of} loops and assignment.

Finally it also fixes some tests in test-js which where passing before
but not now that we have correct behavior :^).
2021-09-30 08:16:32 +01:00
davidot
bfc1b4ba61 LibJS: Allow member expressions in binding patterns
Also allows literal string and numbers as property names in object
binding patterns.
2021-09-30 08:16:32 +01:00
davidot
53cc7e8398 LibJS: Remove unused delete_variable method in VM 2021-09-30 08:16:32 +01:00
Linus Groh
8c81c84c18 LibJS: Convert internal_set_prototype_of() to ThrowCompletionOr 2021-09-29 23:49:53 +01:00
Linus Groh
9043041dd3 LibJS: Move [[BoundThis]] and [[BoundArguments]] to BoundFunction 2021-09-25 17:51:30 +02:00
Linus Groh
4566472ed6 LibJS: Rename BoundFunction::m_target_function to match spec name 2021-09-25 17:51:30 +02:00
Linus Groh
a08292d76c LibJS: Move has_simple_parameter_list to ECMAScriptFunctionObject 2021-09-25 17:51:30 +02:00
Linus Groh
76eb8fe717 LibJS: Move [[Fields]] to ECMAScriptFunctionObject 2021-09-25 17:51:30 +02:00