Commit graph

1458 commits

Author SHA1 Message Date
Linus Groh
1e86379327 LibJS: Rest parameter in setter functions is a syntax error 2020-10-20 20:27:58 +02:00
Linus Groh
6331d45a6f LibJS: Move checks for invalid getter/setter params to parse_function_node
This allows us to provide better error messages as we can point the
syntax error location to the exact first invalid parameter instead of
always the end of the function within a object literal or class
definition.

Before this change:

    const Foo = { set bar() {} }
                               ^
    Uncaught exception: [SyntaxError]: Object setter property must have one argument (line: 1, column: 28)

    class Foo { set bar() {} }
                             ^
    Uncaught exception: [SyntaxError]: Class setter method must have one argument (line: 1, column: 26)

After this change:

    const Foo = { set bar() {} }
                          ^
    Uncaught exception: [SyntaxError]: Setter function must have one argument (line: 1, column: 23)

    class Foo { set bar() {} }
                        ^
    Uncaught exception: [SyntaxError]: Setter function must have one argument (line: 1, column: 21)

The only possible downside of this change is that class getters/setters
and functions in objects are not distinguished in the message anymore -
I don't think that's important though, and classes are (mostly) just
syntactic sugar anyway.
2020-10-20 20:27:58 +02:00
Linus Groh
db75be1119 LibJS: Refactor parse_function_node() bool parameters into bit flags
I'm about to add even more options and a bunch of unnamed true/false
arguments is really not helpful. Let's make this a single parse options
parameter using bit flags.
2020-10-20 20:27:58 +02:00
Linus Groh
a82c56f9f7 LibJS: Speed up IndexedPropertyIterator by computing non-empty indices
This provides a huge speed-up for objects with large numbers as property
keys in some situation. Previously we would simply iterate from 0-<max>
and check if there's a non-empty value at each index - now we're being
smarter and compute a list of non-empty indices upfront, by checking
each value in the packed elements vector and appending the sparse
elements hashmap keys (for GenericIndexedPropertyStorage).

Consider this example, an object with a single own property, which is a
number increasing by a factor of 10 each iteration:

    for (let i = 0; i < 10; ++i) {
        const o = {[10 ** i]: "foo"};
        const start = Date.now();
        Object.getOwnPropertyNames(o);  // <-- IndexedPropertyIterator
        const end = Date.now();
        console.log(`${10 ** i} -> ${(end - start) / 1000}s`);
    }

Before this change:

    1 -> 0.0000s
    10 -> 0.0000s
    100 -> 0.0000s
    1000 -> 0.0000s
    10000 -> 0.0005s
    100000 -> 0.0039s
    1000000 -> 0.0295s
    10000000 -> 0.2489s
    100000000 -> 2.4758s
    1000000000 -> 25.5669s

After this change:

    1 -> 0.0000s
    10 -> 0.0000s
    100 -> 0.0000s
    1000 -> 0.0000s
    10000 -> 0.0000s
    100000 -> 0.0000s
    1000000 -> 0.0000s
    10000000 -> 0.0000s
    100000000 -> 0.0000s
    1000000000 -> 0.0000s

Fixes #3805.
2020-10-20 08:51:41 +02:00
Linus Groh
46cc1f718e LibJS: Unprefixed octal numbers are a syntax error in strict mode 2020-10-19 20:08:22 +02:00
Linus Groh
e898c98873 LibJS: Don't parse arrow function with newline between ) and =>
If there's a newline between the closing paren and arrow it's not a
valid arrow function, ASI should kick in instead (it'll then fail with
"Unexpected token Arrow")
2020-10-19 11:31:55 +02:00
Linus Groh
965d952ff3 LibJS: Share parameter parsing between regular and arrow functions
This simplifies try_parse_arrow_function_expression() and fixes a few
cases that should not produce an arrow function AST but did:

    (a,,) => {}
    (a b) => {}
    (a ...b) => {}
    (...b a) => {}

The new parsing logic checks whether parens are expected and uses
parse_function_parameters() if so, rolling back if a new syntax error
occurs during that. Otherwise it's just an identifier in which case we
parse the single parameter ourselves.
2020-10-19 11:31:55 +02:00
Linus Groh
aa68de3530 LibJS: Fix dump() indentation of UpdateExpression with suffix operator 2020-10-19 11:31:55 +02:00
Linus Groh
2dbea60fe2 LibJS: Multiple 'default' clauses in switch statement are a syntax error 2020-10-19 11:30:14 +02:00
Linus Groh
f8886ef5ba LibJS: Handle continue in switch statement unwinding 2020-10-18 19:08:52 +02:00
Linus Groh
8f54edb7a0 LibJS: Handle return value in switch statement unwinding
Fixes #3790.
2020-10-18 19:08:52 +02:00
Stephan Unverwerth
2c888b3c6e LibJS: Fix parsing of invalid numeric literals
i.e. "1e" "0x" "0b" "0o" used to be parsed as valid literals.
They now produce invalid tokens. Fixes #3716
2020-10-18 15:38:57 +02:00
Andreas Kling
77c1957961 LibJS: Use allocate_without_global_object for allocating Shapes 2020-10-17 23:47:07 +02:00
Andreas Kling
d8269c343c LibJS: Avoid creating temporary Strings to look up tokens while lexing
It would be cool to solve this in a general way so that looking up
a string literal or StringView in a HashMap with String keys avoids
creating a temp string.

For now, this patch simply addresses the issue in JS::Lexer.
This is a 2-3% speed-up on test-js.
2020-10-17 23:44:41 +02:00
Andreas Kling
d3dfd55472 LibJS: Prebake the empty object ({}) with a prototype
Instead of performing a prototype transition for every new object we
create via {}, prebake the object returned by Object::create_empty()
with a shape with ObjectPrototype as the prototype.

We also prebake the shape for the object assigned to the "prototype"
property of new ScriptFunction objects, since those are extremely
common and that code broke from this change anyway.

This avoid a large number of transitions and is a small speed-up on
test-js.
2020-10-17 23:23:53 +02:00
Linus Groh
b98b83712f LibJS: constexpr some Number object constant values 2020-10-16 17:06:57 +02:00
Andreas Kling
2c956ac132 LibJS: Reorganize Shape members to reduce sizeof(Shape) a bit 2020-10-16 16:46:27 +02:00
Andreas Kling
2c0e153396 LibJS: Don't bother deferring GC during ensure_property_table()
This is not actually necessary, since no GC allocations are made during
this process. If we ever make property tables into heap cells, we'd
have to rethink this.
2020-10-16 08:59:51 +02:00
Andreas Kling
4387590e65 LibJS: Support move semantics for StringOrSymbol
This allows us to rehash property tables without a bunch of ref count
churn happening.
2020-10-15 23:49:53 +02:00
Andreas Kling
1d96ecf148 Everywhere: Add missing <AK/TemporaryChange.h> includes
Don't rely on HashTable.h pulling this in.
2020-10-15 23:49:53 +02:00
Linus Groh
e07490ce13 LibJS: Don't assume value for index < size in IndexedPropertyIterator
This assumption only works for the m_packed_elements Vector where a
missing value at a certain index still returns an empty value, but not
for the m_sparse_elements HashMap, which is being used for indices
>= 200 - in that case the Optional<ValueAndAttributes> result will not
have a value.

This fixes a crash in the js REPL where printing an array with a hole at
any index >= 200 would crash.
2020-10-14 00:52:47 +02:00
Andreas Kling
a1029738fd LibJS: Add some more items to CommonPropertyNames that I missed 2020-10-14 00:10:49 +02:00
Andreas Kling
8f535435dc LibJS: Avoid property lookups during object initialization
When we're initializing objects, we're just adding a bunch of new
properties, without transition, and without overlap (we never add
the same property twice.)

Take advantage of this by skipping lookups entirely (no need to see
if we're overwriting an existing property) during initialization.

Another nice test-js speedup :^)
2020-10-13 23:57:45 +02:00
Andreas Kling
7b863330dc LibJS: Cache commonly used FlyStrings in the VM
Roughly 7% of test-js runtime was spent creating FlyStrings from string
literals. This patch frontloads that work and caches all the commonly
used names in LibJS on a CommonPropertyNames struct that hangs off VM.
2020-10-13 23:57:45 +02:00
Andreas Kling
9f6c5f68b6 LibJS: Tidy up CallExpression::execute() a little bit 2020-10-13 19:13:37 +02:00
Linus Groh
a5bf6cfff9 LibJS: Don't change offset when reconfiguring property in unique shape
When changing the attributes of an existing property of an object with
unique shape we must not change the PropertyMetadata offset.
Doing so without resizing the underlying storage vector caused an OOB
write crash.

Fixes #3735.
2020-10-10 23:25:00 +02:00
Matthew Olsson
e8da5f99b1 LibJS: break or continue with nonexistent label is a syntax error 2020-10-08 23:27:16 +02:00
Matthew Olsson
6e05685ad4 LibJS: Fix return statements not working properly in loops
Previously, when a loop detected an unwind of type ScopeType::Function
(which means a return statement was executed inside of the loop), it
would just return undefined. This set the VM's last_value to undefined,
when it should have been the returned value. This patch makes all loop
statements return the appropriate value in the above case.
2020-10-08 23:23:55 +02:00
Matthew Olsson
d980073122 LibJS: Handle unwinding in while and do-while statements
For some reason, this was never added. So something like "while (true)
{ return }" would loop infinitely.
2020-10-08 23:23:55 +02:00
Matthew Olsson
e49ea1b520 LibJS: Disallow 'continue' & 'break' outside of their respective scopes
'continue' is no longer allowed outside of a loop, and an unlabeled
'break' is not longer allowed outside of a loop or switch statement.
Labeled 'break' statements are still allowed everywhere, even if the
label does not exist.
2020-10-08 10:20:49 +02:00
Matthew Olsson
9a82c22a85 LibJS: Disallow 'return' outside of a function 2020-10-08 10:03:21 +02:00
Linus Groh
5feb7e8d28 LibJS: Use PropertyName::from_value() in MemberExpression::computed_property_name()
No need for duplicating this logic.
2020-10-08 10:02:47 +02:00
Linus Groh
bc78e4b7da LibJS: Fix PropertyName::from_value() for negative and non-int numbers
It was converting *any* number to an i32 index, which obviously is not
correct for negative ints, doubles, infinity and nan.

Fixes #3712.
2020-10-08 10:02:47 +02:00
Andreas Kling
c541310e19 LibJS: Use IntrusiveList for Allocator's block lists
This way we don't need to deal with shifting vector storage, and most
operations are upgraded from O(n) to O(1) :^)
2020-10-07 14:07:31 +02:00
Andreas Kling
d1592643a6 LibJS: Make sure the HeapBlock cell storage is alignas(Cell) 2020-10-07 13:09:59 +02:00
Andreas Kling
48f13b7c3f LibJS: Split Heap into per-cell-size allocators
Instead of keeping all the HeapBlocks in one big list, we now split it
into two levels:

- Heap has a set of Allocators, each with a specific cell size.
- Allocators have two lists of blocks, "full" and "usable".

Allocating a new cell no longer has to scan the entire set of blocks,
but instead just needs to find the right allocator and then pop a cell
from its freelist. If all the blocks in the allocator are full, a new
block will be created.

Blocks are moved from the "full" to "usable" list after sweeping has
determined that they are not completely empty and not completely full.

There are certainly many ways we can improve on this. This patch is
mostly about getting the new allocator architecture in place. :^)
2020-10-06 18:50:47 +02:00
Andreas Kling
8baacda03d LibJS: Fix weird self-including header 2020-10-06 18:37:58 +02:00
Andreas Kling
4c33209011 LibJS: Add Object::define_property_without_transition() helper
This allows us to avoid transitioning in two common cases, saving some
time during object construction.
2020-10-06 17:43:51 +02:00
Andreas Kling
148c4161d9 LibJS: Avoid work in Shape::lookup() if there are no properties 2020-10-05 20:53:00 +02:00
Andreas Kling
69bae3fd9a LibJS: Prevent object shape transitions during runtime object buildup
While initialization common runtime objects like functions, prototypes,
etc, we don't really care about tracking transitions for each and every
property added to them.

This patch puts objects into a "disable transitions" mode while we call
initialize() on them. After that, adding more properties will cause new
transitions to be generated and added to the chain.

This gives a ~10% speed-up on test-js. :^)
2020-10-05 20:53:00 +02:00
Andreas Kling
50ab87f651 LibJS: Make use of existing property tables when reifying new ones
When reifying a shape transition chain, look for the nearest previous
shape in the transition chain that has a property table already, and
use that as the starting point.

This achieves two things:

1. We do less work when reifying property tables that already have
   partial property tables earlier in the chain.

2. This enables adding properties to a shape without performing a
   transition. This will be useful for initializing runtime objects
   with way fewer allocations. See next patch. :^)
2020-10-05 20:53:00 +02:00
Linus Groh
aa71dae03c LibJS: Implement logical assignment operators (&&=, ||=, ??=)
TC39 proposal, stage 4 as of 2020-07.
https://tc39.es/proposal-logical-assignment/
2020-10-05 17:57:26 +02:00
Nico Weber
d8d00d3ac7 LibJS: Add StringOrSymbol::as_string_impl() helper 2020-10-05 17:35:27 +02:00
Nico Weber
cc765e14ca AK: Move StringImpl::operator== implementation into StringImpl 2020-10-05 17:35:27 +02:00
Linus Groh
2d4cd5b49b LibJS: Evaluate AssignmentExpression LHS before RHS according to the spec
Fixes #3689.
2020-10-05 14:34:37 +02:00
Linus Groh
f4d0babd5d LibJS: Make assignment to CallExpression a syntax error in strict mode 2020-10-05 09:25:04 +02:00
Linus Groh
283ee678f7 LibJS: Validate all assignment expressions, not just "="
The check for invalid lhs and assignment to eval/arguments in strict
mode should happen for all kinds of assignment expressions, not just
AssignmentOp::Assignment.
2020-10-05 09:25:04 +02:00
Linus Groh
e80217a746 LibJS: Unify syntax highlighting
So far we have three different syntax highlighters for LibJS:

- js's Line::Editor stylization
- JS::MarkupGenerator
- GUI::JSSyntaxHighlighter

This not only caused repetition of most token types in each highlighter
but also a lot of inconsistency regarding the styling of certain tokens:

- JSSyntaxHighlighter was considering TokenType::Period to be an
  operator whereas MarkupGenerator categorized it as punctuation.
- MarkupGenerator was considering TokenType::{Break,Case,Continue,
  Default,Switch,With} control keywords whereas JSSyntaxHighlighter just
  disregarded them
- MarkupGenerator considered some future reserved keywords invalid and
  others not. JSSyntaxHighlighter and js disregarded most

Adding a new token type meant adding it to ENUMERATE_JS_TOKENS as well
as each individual highlighter's switch/case construct.

I added a TokenCategory enum, and each TokenType is now associated to a
certain category, which the syntax highlighters then can use for styling
rather than operating on the token type directly. This also makes
changing a token's category everywhere easier, should we need to do that
(e.g. I decided to make TokenType::{Period,QuestionMarkPeriod}
TokenCategory::Operator for now, but we might want to change them to
Punctuation.
2020-10-04 23:41:31 +02:00
Andreas Kling
fdb0ac7c1e LibJS: Remove some unused Interpreter member functions 2020-10-04 23:10:07 +02:00
Andreas Kling
94b95a4924 LibJS: Remove Interpreter::call()
Just use VM::call() directly everywhere.
2020-10-04 23:08:49 +02:00