Commit graph

93 commits

Author SHA1 Message Date
Andreas Kling
9d21d88374 LibJS: Allow let and const to create locals in global code
This is actually safe everywhere but in the topmost program scope.
For web compatibility reasons, we have to flush all top-level bindings
to the environment, in case a subsequent separate <script> program
comes looking for them.
2024-06-14 16:08:02 +02:00
Aliaksandr Kalenik
e934132442 LibJS+LibWeb: Pass function metadata collected in parsing using a struct
By using separate struct we can avoid updating AST node and
ECMAScriptFunctionObject constructors every time there is a need to
add or remove some additional information colllected during parsing.
2024-05-23 09:53:31 +02:00
Aliaksandr Kalenik
ebb3d8025c LibJS: Get this from execution context for non-arrow functions
Allows to skip function environment allocation for non-arrow functions
if the only reason it is needed is to hold `this` binding.

The parser is changed to do following:
- If a function is an arrow function and uses `this` then all functions
  in a scope chain are marked to allocate function environment for
  `this` binding.
- If a function uses `new.target` then all functions in a scope chain
  are marked to allocate function environment.

`ordinary_call_bind_this()` is changed to put `this` value in execution
context when function environment allocation is skipped.

35% improvement in Octane/typescript.js
50% improvement in Octane/deltablue.js
19% improvement in Octane/raytrace.js
2024-05-22 18:30:13 +02:00
Aliaksandr Kalenik
4d5823a5bc LibWeb+LibJS: Skip function environment allocation if possible
If a function has the following properties:
- uses only local variables and registers
- does not use `this`
- does not use `new.target`
- does not use `super`
- does not use direct eval() calls

then it is possible to entirely skip function environment allocation
because it will never be used

This change adds gathering of information whether a function needs to
access `this` from environment and updates `prepare_for_ordinary_call()`
to skip allocation when possible.

For now, this optimisation is too aggressively blocked; e.g. if `this`
is used in a function scope, then all functions in outer scopes have to
allocate an environment. It could be improved in the future, although
this implementation already allows skipping >80% of environment
allocations on Discord, GitHub and Twitter.
2024-05-04 06:48:07 +02:00
Ali Mohammad Pur
5e1499d104 Everywhere: Rename {Deprecated => Byte}String
This commit un-deprecates DeprecatedString, and repurposes it as a byte
string.
As the null state has already been removed, there are no other
particularly hairy blockers in repurposing this type as a byte string
(what it _really_ is).

This commit is auto-generated:
  $ xs=$(ack -l \bDeprecatedString\b\|deprecated_string AK Userland \
    Meta Ports Ladybird Tests Kernel)
  $ perl -pie 's/\bDeprecatedString\b/ByteString/g;
    s/deprecated_string/byte_string/g' $xs
  $ clang-format --style=file -i \
    $(git diff --name-only | grep \.cpp\|\.h)
  $ gn format $(git ls-files '*.gn' '*.gni')
2023-12-17 18:25:10 +03:30
Andreas Kling
07f567cd9f LibJS+LibWeb: Another round of bringing module loading closer to spec
In particular, this patch focuses on:
- Updating the old "import assertions" to the new "import attributes"
- Allowing realms as module import referrer
2023-12-03 20:46:55 +01:00
Andreas Kling
ffe304e88b LibJS: Don't create arguments object due to o.arguments access
When deciding whether we need to create a full-blown `arguments` object,
we look at various things, starting as early as in the parser.

Until now, if the parser saw the identifier `arguments`, we'd decide
that it's enough of a clue that we should create the `arguments` object
since somebody is obviously accessing it.

However, that missed the case where someone is just accessing a property
named `arguments` on some object. In such cases (`o.arguments`), we now
hold off on creating an `arguments` object.

~11% speed-up on Octane/typescript.js :^)
2023-11-16 13:26:21 +01:00
Aliaksandr Kalenik
b0a533dbc0 LibJS: Identify global variables during parsing
Identifying global variables during parsing will make it possible to
generate special optimized instruction to access them in upcoming
changes.
2023-07-12 16:03:16 +02:00
Aliaksandr Kalenik
2f85faef0f LibJS: Fix scope detection for ids in default function params
This change fixes an issue where identifiers used in default function
parameters were being "registered" in the function's parent scope
instead of its own scope. This bug resulted in incorrectly detected
local variables. (Variables used in the default function parameter
expression should be considered 'captured by nested function'.)

To resolve this issue, the function scope is now created before parsing
function parameters. Since function parameters can no longer be passed
in the constructor, a setter function has been introduced to set them
later, when they are ready.
2023-07-08 14:03:12 +02:00
Aliaksandr Kalenik
380abddf3c LibJS: Update parser to detect if identifier refer a "local" variable
This modification enables the parser to determine whether an identifier
used within a function refers to a local variable or not. In this
context, a local identifier means that it is not captured by any nested
function declaration which means it modified only from inside a
function.

The information about whether identifier is local is stored inside
Identifier AST node and also contains information about the index of
local variable inside a function and information about total number
of local variables used by a function is stored in function nodes.
2023-07-05 21:03:01 +02:00
Simon Wanner
a2efecac03 LibJS: Parse slashes after reserved identifiers correctly
Previously we were unable to parse code like `yield/2` because `/2`
was parsed as a regex. At the same time `for (a in / b/)` was parsed
as a division.

This is solved by defaulting to division in the lexer, but calling
`force_slash_as_regex()` from the parser whenever an IdentifierName
is parsed as a ReservedWord.
2023-06-10 07:20:33 +02:00
Andreas Kling
bd5d8e9d35 LibJS: Make RefPtr and NonnullRefPtr usage const-correct
This mainly affected the AST, which is now const throughout.
2023-02-21 00:54:04 +01:00
davidot
bff038411a LibJS: Add using declaration support in for and for of loops
The using declarations have kind of special behavior in for loops so
this is seperated.
2023-01-23 09:56:50 +00:00
davidot
541637e15a LibJS: Add using declaration support, RAII like operation in js
In this patch only top level and not the more complicated for loop using
statements are supported. Also, as noted in the latest meeting of tc39
async parts of the spec are not stage 3 thus not included.
2023-01-23 09:56:50 +00: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
Linus Groh
57dc179b1f Everywhere: Rename to_{string => deprecated_string}() where applicable
This will make it easier to support both string types at the same time
while we convert code, and tracking down remaining uses.

One big exception is Value::to_string() in LibJS, where the name is
dictated by the ToString AO.
2022-12-06 08:54:33 +01:00
Linus Groh
6e19ab2bbc AK+Everywhere: Rename String to DeprecatedString
We have a new, improved string type coming up in AK (OOM aware, no null
state), and while it's going to use UTF-8, the name UTF8String is a
mouthful - so let's free up the String name by renaming the existing
class.
Making the old one have an annoying name will hopefully also help with
quick adoption :^)
2022-12-06 08:54:33 +01:00
davidot
2c26ee89ac LibJS: Remove m_first_invalid_property_range from ObjectExpression
This was state only used by the parser to output an error with
appropriate location. This shrinks the size of ObjectExpression from
120 bytes down to just 56. This saves roughly 2.5 MiB when loading
twitter.
2022-11-27 12:31:37 +01:00
Andreas Kling
835d7aac96 LibJS: Make FunctionNode::Parameter be a standalone FunctionParameter
This will allow us to forward declare it and avoid including AST.h in a
number of places.
2022-11-23 16:05:59 +00:00
Andreas Kling
e6331031c4 LibJS: Make Parser::Error a standalone ParserError class
This allows us to forward declare it and reduce the number of things
that need to include Parser.h.
2022-11-23 16:05:59 +00:00
Andreas Kling
b0b022507b LibJS: Reduce AST memory usage by shrink-wrapping source range info
Before this change, each AST node had a 64-byte SourceRange member.
This SourceRange had the following layout:

    filename:       StringView (16 bytes)
    start:          Position (24 bytes)
    end:            Position (24 bytes)

The Position structs have { line, column, offset }, all members size_t.

To reduce memory consumption, AST nodes now only store the following:

    source_code:    NonnullRefPtr<SourceCode> (8 bytes)
    start_offset:   u32 (4 bytes)
    end_offset:     u32 (4 bytes)

SourceCode is a new ref-counted data structure that keeps the filename
and original parsed source code in a single location, and all AST nodes
have a pointer to it.

The start_offset and end_offset can be turned into (line, column) when
necessary by calling SourceCode::range_from_offsets(). This will walk
the source code string and compute line/column numbers on the fly, so
it's not necessarily fast, but it should be rare since this information
is primarily used for diagnostics and exception stack traces.

With this, ASTNode shrinks from 80 bytes to 32 bytes. This gives us a
~23% reduction in memory usage when loading twitter.com/awesomekling
(330 MiB before, 253 MiB after!) :^)
2022-11-22 21:13:35 +01:00
demostanis
3e8b5ac920 AK+Everywhere: Turn bool keep_empty to an enum in split* functions 2022-10-24 23:29:18 +01:00
davidot
9f661d20f7 LibJS: Allow anonymous functions as default exports
This requires a special case with names as the default function is
supposed to have a unique name ("*default*" in our case) but when
checked should have name "default".
2022-09-02 02:07:37 +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
e5adc51e27 LibJS: Allow invalid string in tagged template literals
Since tagged template literals can inspect the raw string it is not a
syntax error to have invalid escapes. However the cooked value should be
`undefined`.
We accomplish this by tracking whether parse_string_literal
fails and then using a NullLiteral (since UndefinedLiteral is not a
thing) and finally converting null in tagged template execution to
undefined.
2022-08-17 23:56:24 +01:00
sin-ack
3f3f45580a Everywhere: Add sv suffix to strings relying on StringView(char const*)
Each of these strings would previously rely on StringView's char const*
constructor overload, which would call __builtin_strlen on the string.
Since we now have operator ""sv, we can replace these with much simpler
versions. This opens the door to being able to remove
StringView(char const*).

No functional changes.
2022-07-12 23:11:35 +02:00
DexesTTP
eae8520dbb LibJS: Properly compute the line for source location hints
These were obvious wrong uses of the old default "only first occurence"
parameter that was used in String::replace.
2022-07-06 11:12:45 +02:00
DexesTTP
7ceeb74535 AK: Use an enum instead of a bool for String::replace(all_occurences)
This commit has no behavior changes.

In particular, this does not fix any of the wrong uses of the previous
default parameter (which used to be 'false', meaning "only replace the
first occurence in the string"). It simply replaces the default uses by
String::replace(..., ReplaceMode::FirstOnly), leaving them incorrect.
2022-07-06 11:12:45 +02:00
Luke Wilde
34f902fb52 LibJS: Add missing steps and spec comments to PerformEval
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.
2022-04-11 21:23:36 +01:00
Idan Horowitz
086969277e Everywhere: Run clang-format 2022-04-01 21:24:45 +01:00
Anonymous
602190f66f LibJS: Fix mixing of logical and coalescing operators
The same expression is not allowed to contain both the
logical && and || operators, and the coalescing ?? operator.

This patch changes how "forbidden" tokens are handled, using a
finite set instead of an Vector. This supports much more efficient
merging of the forbidden tokens when propagating forward, and
allowing the return of forbidden tokens to parent contexts.
2022-02-16 11:18:41 +00:00
Anonymous
f55d7bf6f3 LibJS: Fix cases where we incorrectly allowed 'in' in for loops
We needed to propagate the forbidden token set to all parsing functions
that can call back into parse_expression.
2022-02-15 10:31:41 +00:00
Andreas Kling
50a446a5d1 LibJS: Make more use of Token::flystring_value()
This patch makes check_identifier_name_for_assignment_validity()
take a FlyString instead of a StringView. We then exploit this by
passing FlyString in more places via flystring_value().

This gives a ~1% speedup when parsing the largest Discord JS file.
2022-02-13 14:44:36 +01:00
Linus Groh
bc183dbbcb LibJS: Replace uses of MarkedValueList with MarkedVector<Value>
This is effectively a drop-in replacement.
2022-02-09 12:25:27 +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
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
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
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
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
e69276e704 LibJS: Implement parsing and executing for-await-of loops 2021-11-29 15:20:07 +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
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
davidot
16cc82460f LibJS: Add parsing and evaluation of private fields and methods 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
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