Commit graph

152 commits

Author SHA1 Message Date
davidot
99edf5b25a LibJS: Track whether a program has a top level await statement 2022-01-22 01:21:18 +00:00
davidot
aca427fc8c LibJS: Make parsing import and export entries follow the spec
The big changes are:
- Allow strings as Module{Export, Import}Name
- Properly track declarations in default export statements

However, the spec is a little strange in that it allows function and
class declarations without a name in default export statements.
This is quite hard to fully implement without rewriting more of the
parser so for now this behavior is emulated by faking things with
function and class expressions. See the comments in
parse_export_statement for details on the hacks and where it goes wrong.
2022-01-22 01:21:18 +00:00
Linus Groh
531d1ac734 LibJS: Capture source text of FunctionNode and ClassExpression 2022-01-19 20:33:08 +00:00
Linus Groh
95a9f12b97 LibJS: Set Token's m_offset to the value's start index
This makes much more sense than the current way of setting it to the
Lexer's m_position after consuming the full value.
2022-01-19 20:33:08 +00:00
Linus Groh
e8519156bc LibJS: Implement create_dynamic_function() according to the spec
The three major changes are:

- Parsing parameters, the function body, and then the full assembled
  function source all separately. This is required by the spec, as
  function parameters and body must be valid each on their own, which
  cannot be guaranteed if we only ever parse the full function.
- Returning an ECMAScriptFunctionObject instead of a FunctionExpression
  that needs to be evaluated separately. This vastly simplifies the
  {Async,AsyncGenerator,Generator,}Function constructor implementations.
  Drop '_node' from the function name accordingly.
- The prototype is now determined via GetPrototypeFromConstructor and
  passed to OrdinaryFunctionCreate.
2022-01-16 01:54:48 +01:00
Linus Groh
f0b2179bd7 LibJS: Don't require ParenClose in Parser::parse_formal_parameters()
The parentheses are dealt with outside this function, so we shouldn't
use the (non)existence of one as the condition for consuming another
comma and then parameter. Just check for a comma instead. This becomes
relevant when parsing standalone function parameters as per the spec in
the CreateDynamicFunction AO.
2022-01-16 01:54:48 +01:00
Linus Groh
e730ada07d LibJS: Consume curly braces outside of Parser::parse_function_body()
The curly braces are not part of the FunctionBody production. This
becomes relevant when parsing a standalone function body as per the spec
in the CreateDynamicFunction AO.
2022-01-16 01:54:48 +01:00
Linus Groh
0c73fbbba5 LibJS: Rename FunctionKind::{Regular => Normal}
This is what CreateDynamicFunction calls it.
2022-01-16 01:54:48 +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
davidot
e179cf2540 LibJS: Don't VERIFY that the token after 'import' is one of '.' and '('
Although those are the only valid options parse_primary_expression is
sometimes called when only an expression is valid which means it did not
check match_expression and might fail the now removed VERIFY.
2021-12-29 16:57:23 +01:00
davidot
be3b806487 LibJS: Parse assert clauses of in- and export statements
Based on proposal: https://tc39.es/proposal-import-assertions
Since imports are not supported yet this is not functional.
2021-12-21 14:04:23 +01:00
davidot
81312986fe LibJS: Disallow async generator functions called 'await' or 'yield' 2021-12-21 14:04:23 +01:00
davidot
c8e80690a7 LibJS: Don't treat yield after void as identifier in generator functions 2021-12-21 14:04:23 +01:00
davidot
45578f58dc LibJS: Allow super property lookup and new.target in static init blocks 2021-12-21 14:04:23 +01:00
davidot
5010d4c20c LibJS: Don't match async \n function as an async function declaration 2021-11-30 17:05:32 +00:00
davidot
c2ebaa9d87 LibJS: Replace the verify in private identifier with a syntax error
Since sometimes expressions are parsed without checking we can hit this
expression without it being followed by an 'in'.
2021-11-30 17:05:32 +00:00
davidot
b7c7d54167 LibJS: Split parsing program to script and module separately
This allows us to only perform checks like export bindings existing only
for modules. Also this makes it easier to set strict and other state
variables with TemporaryChanges.
2021-11-30 17:05:32 +00:00
davidot
22174d3b7b LibJS: Rename in_async_function_context to await_expression_is_valid
Since await can be valid in module code which is not an async function
the old name is not really representative for the usage.
2021-11-30 17:05:32 +00:00
davidot
045a42cf35 LibJS: Parse dynamic import calls 'import()' and 'import.meta'
For now both just throw when executing but this can be implemented when
modules are implemented :^).
2021-11-30 17:05:32 +00:00
davidot
73eb29dabe LibJS: Lookahead for a period when parsing new.target
This allows us to skip saving and loading the state whenever we parse
'new'.
2021-11-30 17:05:32 +00:00
davidot
4d7e79fb72 LibJS: Stop parsing an expression on comma after a yield 2021-11-30 17:05:32 +00:00
davidot
156dfe3d62 LibJS: Disallow member expression in binding pattern as parameters 2021-11-30 17:05:32 +00:00
davidot
51e23cd043 LibJS: Disallow shorthand properties with reserved names 2021-11-30 17:05:32 +00:00
davidot
e491fc0e81 LibJS: Allow defining class fields with "keyword" names 2021-11-30 17:05:32 +00:00
davidot
cbbfcd35e7 LibJS: Disallow await keywords in static init blocks
In static init blocks 'await' cannot be used. Note that this does not
cover all the cases since the parser currently cannot distinguish
between expressions within parenthesis and direct expressions.
2021-11-30 17:05:32 +00:00
davidot
c57721cf83 LibJS: Allow escaped 'async' as identifier
Since 'async' is only special if it occurs before a function it can be
used as escaped identifier in all cases.
2021-11-30 17:05:32 +00:00
davidot
e751dcea43 LibJS: Treat private identifier as divisible token
And also make sure private identifiers are correctly checked when
synthesizing a binding pattern.
2021-11-30 17:05:32 +00:00
davidot
32016d3924 LibJS: Allow object properties called 'async' 2021-11-30 17:05:32 +00:00
davidot
e69276e704 LibJS: Implement parsing and executing for-await-of loops 2021-11-29 15:20:07 +00:00
davidot
0982a73d1d LibJS: Parse async generator functions 2021-11-21 21:46:39 +00:00
davidot
de46a2cff1 LibJS: Parse async arrow functions 2021-11-21 21:46:39 +00:00
Andreas Kling
8b1108e485 Everywhere: Pass AK::StringView by value 2021-11-11 01:27:46 +01:00
Idan Horowitz
d5f637fa21 LibJS: Do not parse async methods with a new line after the "async"
This was already checked in normal function expressions, but was
missing for Object Expressions.
2021-11-10 18:11:26 +00:00
Idan Horowitz
46dabf02ec LibJS: Add support for await expressions 2021-11-10 08:48:27 +00:00
Idan Horowitz
681787de76 LibJS: Add support for async functions
This commit adds support for the most bare bones version of async
functions, support for async generator functions, async arrow functions
and await expressions are TODO.
2021-11-10 08:48:27 +00: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
davidot
2d48529073 LibJS: Implement private identifiers in optional chains 2021-10-20 23:19:17 +01:00
davidot
4c8090a45d LibJS: Use ClassFieldInitializerStatement for class fields
This is necessary as we might have to perform named evaluation with the
field name.
Ideally we would also skip some setup parts of the function like
function_declaration_instantiation however this would require bigger
changes to ECMAScriptFunctionObject.
2021-10-20 23:19:17 +01:00
davidot
7e8724db71 LibJS: Add support for the '#privateName in obj' expression 2021-10-20 23:19:17 +01:00
davidot
16cc82460f LibJS: Add parsing and evaluation of private fields and methods 2021-10-20 23:19:17 +01:00
davidot
6b2accce31 LibJS: Add static initializers to classes 2021-10-20 23:19:17 +01:00
davidot
1245512c50 LibJS: Make class definition evaluation more spec like in ordering 2021-10-20 23:19:17 +01:00
davidot
9394cbcd24 LibJS: Do not save state for peeking at the next token from the lexer
This saves having to save and load the parser state.
This could give an incorrect token in some cases where the parser
communicates to the lexer. However this is not applicable in any of the
current usages and this would require one to parse the current token
as normal which is exactly what you don't want to do in that scenario.
2021-10-15 10:27:16 +01:00
Ali Mohammad Pur
13138811df LibJS: Partially revert 12b283f
This commit partially reverts "LibJS: Make accessing the current
function's arguments cheaper".
While the change passed all the currently passing test262 tests, it
seems to have _some_ flaw that silently breaks with some real-world
websites.
As the speedup with negligible at best, let's just revert it until we
can implement it more correctly.
2021-10-08 19:56:02 +03:30
Andreas Kling
b2de563166 LibJS: Propagate "contains direct call to eval()" flag from parser
We now propagate this flag to FunctionDeclaration, and then also into
ECMAScriptFunctionObject.

This will be used to disable optimizations that aren't safe in the
presence of direct eval().
2021-10-08 12:43:38 +02:00
Ali Mohammad Pur
12b283f32f LibJS: Make accessing the current function's arguments cheaper
Instead of going through an environment record, make arguments of the
currently executing function generate references via the argument index,
which can later be resolved directly through the ExecutionContext.
2021-10-08 12:25:24 +02:00
Ali Mohammad Pur
da296ffd56 LibJS: Treat the Catch binding identifier as a var binding 2021-10-08 12:25:24 +02:00
Linus Groh
4fa5748093 LibJS: Add an optimization to avoid needless arguments object creation
This gives FunctionNode a "might need arguments object" boolean flag and
sets it based on the simplest possible heuristic for this: if we
encounter an identifier called "arguments" or "eval" up to the next
(nested) function declaration or expression, we won't need an arguments
object. Otherwise, we *might* need one - the final decision is made in
the FunctionDeclarationInstantiation AO.

Now, this is obviously not perfect. Even if you avoid eval, something
like `foo.arguments` will still trigger a false positive - but it's a
start and already massively cuts down on needlessly allocated objects,
especially in real-world code that is often minified, and so a full
"arguments" identifier will be an actual arguments object more often
than not.

To illustrate the actual impact of this change, here's the number of
allocated arguments objects during a full test-js run:

Before:
- Unmapped arguments objects: 78765
- Mapped arguments objects: 2455

After:
- Unmapped arguments objects: 18
- Mapped arguments objects: 37

This results in a ~5% speedup of test-js on my Linux host machine, and
about 3.5% on i686 Serenity in QEMU (warm runs, average of 5).

The following microbenchmark (calling an empty function 1M times) runs
25% faster on Linux and 45% on Serenity:

    function foo() {}
    for (var i = 0; i < 1_000_000; ++i)
        foo();

test262 reports no changes in either direction, apart from a speedup :^)
2021-10-05 10:15:14 +01: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