This is a continuation of the previous five commits.
A first big step into the direction of no longer having to pass a realm
(or currently, a global object) trough layers upon layers of AOs!
Unlike the create() APIs we can safely assume that this is only ever
called when a running execution context and therefore current realm
exists. If not, you can always manually allocate the Error and put it in
a Completion :^)
In the spec, throw exceptions implicitly use the current realm's
intrinsics as well: https://tc39.es/ecma262/#sec-throw-an-exception
This is a continuation of the previous four commits.
Passing a global object here is largely redundant, we definitely need
the interpreter but can get the VM and (later) current active realm from
there - and also the global object while we still need it, although I'd
like to remove Interpreter::global_object() in the future.
This now matches the bytecode interpreter's execute_impl() functions.
This is a continuation of the previous three commits.
Now that create() receives the allocating realm, we can simply forward
that to allocate(), which accounts for the majority of these changes.
Additionally, we can get rid of the realm_from_global_object() in one
place, with one more remaining in VM::throw_completion().
This is a continuation of the previous two commits.
As allocating a JS cell already primarily involves a realm instead of a
global object, and we'll need to pass one to the allocate() function
itself eventually (it's bridged via the global object right now), the
create() functions need to receive a realm as well.
The plan is for this to be the highest-level function that actually
receives a realm and passes it around, AOs on an even higher level will
use the "current realm" concept via VM::current_realm() as that's what
the spec assumes; passing around realms (or global objects, for that
matter) on higher AO levels is pointless and unlike for allocating
individual objects, which may happen outside of regular JS execution, we
don't need control over the specific realm that is being used there.
This is a continuation of the previous commit.
Calling initialize() is the first thing that's done after allocating a
cell on the JS heap - and in the common case of allocating an object,
that's where properties are assigned and intrinsics occasionally
accessed.
Since those are supposed to live on the realm eventually, this is
another step into that direction.
The change in 3ec0183 wasn't actually correct, the spec tells us to set
the "prototype" property of the function (created with a prototype of
%GeneratorFunction.prototype% itself) to a newly created object:
OrdinaryObjectCreate(%GeneratorFunction.prototype.prototype%)
This was defined twice, despite being the very same thing:
- ClassElement::ClassFieldDefinition
- ECMAScriptFunctionObject::InstanceField
Move the former to a new header and use it everywhere. Also update the
define_field() AO to take a single field instead of separate name and
initializer arguments.
Having all spec comments verbatim on their own line with no additions
made by us will make it easier to automate comparing said comments to
their current spec counterparts.
While adding spec comments to PerformEval, I noticed we were missing
multiple steps.
Namely, these were:
- Checking if the host will allow us to compile the string
(allowing LibWeb to perform CSP for eval)
- The parser's initial state depending on the environment around us
on direct eval:
- Allowing new.target via eval in functions
- Allowing super calls and super properties via eval in classes
- Disallowing the use of the arguments object in class field
initializers at eval's parse time
- Setting ScriptOrModule of eval's execution context
The spec allows us to apply the additional parsing steps in any order.
The method I have gone with is passing in a struct to the parser's
constructor, which overrides the parser's initial state to (dis)allow
the things stated above from the get-go.
Now we emit CreateVariable and SetVariable with the appropriate
initialization/environment modes, much closer to the spec.
This makes a whole lot of things like let/const variables, function
and variable hoisting and some other things work :^)
Instead of crashing on the spot, return a descriptive error that will
eventually continue its days as a javascript "InternalError" exception.
This should make random crashes with BC less likely.
This commit removes all exception related code:
Remove VM::exception(), VM::throw_exception() etc. Any leftover
throw_exception calls are moved to throw_completion.
The one method left is clear_exception() which is now a no-op. Most of
these calls are just to clear whatever exception might have been thrown
when handling a Completion. So to have a cleaner commit this will be
removed in a next commit.
It also removes the actual Exception and TemporaryClearException classes
since these are no longer used.
In any spot where the exception was actually used an attempt was made to
preserve that behavior. However since it is no longer tracked by the VM
we cannot access exceptions which were thrown in previous calls.
There are two such cases which might have different behavior:
- In Web::DOM::Document::interpreter() the on_call_stack_emptied hook
used to print any uncaught exception but this is now no longer
possible as the VM does not store uncaught exceptions.
- In js the code used to be interruptable by throwing an exception on
the VM. This is no longer possible but was already somewhat fragile
before as you could happen to throw an exception just before a VERIFY.
This removes a number of vm.exception() checks which are now caught
directly by TRY. Make use of these checks in
{Global, Eval}DeclarationInstantiation and while we're here add spec
comments.
Using an Optional was extremely wasteful for function objects that don't
even have a bytecode executable.
This allows ECMAScriptFunctionObject to fit in a smaller size class.
Given we usually call objects Foo{Object,Constructor,Prototype} or
Foo{,Constructor,Prototype}, this name was an odd choice.
The new one matches the spec better, which calls it the "Generator
Prototype Object", so we simply omit the Object suffix as usual as it's
implied.
This should have been the default as it roughly represents the
OrdinaryFunctionCreate AO.
For now, keep two overloads and continue to guess the required prototype
from the function kind in most cases. The prototype needs to be passed
in explicitly when it may be derived from user code, such as in the
CreateDynamicFunction AO.
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.
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.
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 :^)
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.
This is now as defined in the spec. However since we execute async
functions in bytecode by transforming it to a generator function it must
have a prototype for the GeneratorObject. We check whether it is an
async function and in that case use the hardcoded generator object
prototype. This also ensures that user code cannot override this
property thus preventing exposing internal implementation details.
When the bytecode interpreter was converted to ThrowCompletionOr<Value>
it then also cleared the vm.exception() making it seem like no exception
was thrown.
Also removed the TRY_OR_DISCARD as that would skip the error handling
parts.