Commit graph

95 commits

Author SHA1 Message Date
Timothy Flynn
860a37640b LibJS: Convert GetIterator AO to ThrowCompletionOr 2021-10-21 00:26:45 +01:00
davidot
13ead80ee6 LibJS: Add PrivateEnvironment 2021-10-20 23:19:17 +01:00
Idan Horowitz
545d403f6b LibJS: Convert Value operator AOs to ThrowCompletionOr 2021-10-18 23:06:11 +01:00
Idan Horowitz
48ac15758e LibJS: Convert is_loosely_equal() to ThrowCompletionOr 2021-10-18 23:06:11 +01:00
Idan Horowitz
c488f5a59d LibJS: Convert to_property_key() to ThrowCompletionOr 2021-10-17 12:12:35 +01:00
Idan Horowitz
b8f101888b LibJS: Convert to_numeric() 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
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
Linus Groh
364dd42fc8 LibJS: Convert create_data_property_or_throw() to ThrowCompletionOr 2021-10-03 20:14:03 +01:00
Linus Groh
1d45541278 LibJS: Convert Object::set() to ThrowCompletionOr 2021-10-03 20:14:03 +01: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
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
Linus Groh
ee8380edea LibJS: Convert internal_own_property_keys() to ThrowCompletionOr 2021-09-29 23:49:53 +01:00
Linus Groh
e37cf73300 LibJS: Rename OrdinaryFunctionObject to ECMAScriptFunctionObject
The old name is the result of the perhaps somewhat confusingly named
abstract operation OrdinaryFunctionCreate(), which creates an "ordinary
object" (https://tc39.es/ecma262/#ordinary-object) in contrast to an
"exotic object" (https://tc39.es/ecma262/#exotic-object).

However, the term "Ordinary Function" is not used anywhere in the spec,
instead the created object is referred to as an "ECMAScript Function
Object" (https://tc39.es/ecma262/#sec-ecmascript-function-objects), so
let's call it that.

The "ordinary" vs. "exotic" distinction is important because there are
also "Built-in Function Objects", which can be either implemented as
ordinary ECMAScript function objects, or as exotic objects (our
NativeFunction).

More work needs to be done to move a lot of infrastructure to
ECMAScriptFunctionObject in order to make FunctionObject nothing more
than an interface for objects that implement [[Call]] and optionally
[[Construct]].
2021-09-25 17:51:30 +02:00
Linus Groh
580a7e0f7c LibJS: Rename abstract_eq() to is_loosely_equal()
This got turned into a proper AO with a new name recently.

See: https://github.com/tc39/ecma262/commit/c7d6d1c
2021-09-24 09:13:57 +02:00
Linus Groh
c7ff89891c LibJS: Rename strict_eq() to is_strictly_equal()
This got turned into a proper AO with a new name recently.

See: https://github.com/tc39/ecma262/commit/19d7ca4
2021-09-24 09:13:57 +02:00
Idan Horowitz
ab594e5f2f LibJS: Convert Value::invoke and VM::call to ThrowCompletionOr 2021-09-23 23:59:13 +03:00
Brian Gianforcaro
53166c10ca LibJS: Remove unused header includes 2021-08-01 08:10:16 +02:00
Timothy Flynn
a0c19deb80 LibJS: Implement RegExpCreate/RegExpInitialize closer to the spec
RegExpInitialize specifies how the pattern string should be created
before passing it to [[RegExpMatcher]]. Rather than passing it as-is,
the string should be converted to code points and back to a "List" (if
the Unicode flag is present), or as a "List" of UTF-16 code units.
Further. the spec requires that we keep both the original pattern string
and this parsed string in the RegExp object.

The caveat is that the LibRegex parser further requires any multi-byte
code units to be escaped (as "\unnnn"). Otherwise, the code unit is
recognized as individual UTF-8 bytes.
2021-07-23 23:06:57 +01:00
Idan Horowitz
8d01d43f5e LibJS: Replace the boolean argument of Object::set with an enum class
This is more serenity-esque and also makes pointing out missing
exception checks during reviews much easier.
2021-07-16 17:50:01 +01:00
Idan Horowitz
e3ef241108 LibJS: Remove the non-standard put helper and replace it's usages
This removes all usages of the non-standard put helper method and
replaces all of it's usages with the specification required alternative
or with define_direct_property where appropriate.
2021-07-06 14:20:30 +01:00
Idan Horowitz
a6b8291a9b LibJS: Add define_direct_property and remove the define_property helper
This removes all usages of the non-standard define_property helper
method and replaces all it's usages with the specification required
alternative or with define_direct_property where appropriate.
2021-07-06 14:20:30 +01:00
Linus Groh
9555ca99a0 LibJS: Remove unnecessary value_or() from get()
Object::get() never returns an empty value anymore, as per the spec, so
having a value_or() fallback is no longer needed.
2021-07-05 00:03:25 +02:00
Linus Groh
09bd5f8772 LibJS: Rewrite most of Object for spec compliance :^)
This is a huge patch, I know. In hindsight this perhaps could've been
done slightly more incremental, but I started and then fixed everything
until it worked, and here we are. I tried splitting of some completely
unrelated changes into separate commits, however. Anyway.

This is a rewrite of most of Object, and by extension large parts of
Array, Proxy, Reflect, String, TypedArray, and some other things.

What we already had worked fine for about 90% of things, but getting the
last 10% right proved to be increasingly difficult with the current code
that sort of grew organically and is only very loosely based on the
spec - this became especially obvious when we started fixing a large
number of test262 failures.

Key changes include:

- 1:1 matching function names and parameters of all object-related
  functions, to avoid ambiguity. Previously we had things like put(),
  which the spec doesn't have - as a result it wasn't always clear which
  need to be used.
- Better separation between object abstract operations and internal
  methods - the former are always the same, the latter can be overridden
  (and are therefore virtual). The internal methods (i.e. [[Foo]] in the
  spec) are now prefixed with 'internal_' for clarity - again, it was
  previously not always clear which AO a certain method represents,
  get() could've been both Get and [[Get]] (I don't know which one it
  was closer to right now).
  Note that some of the old names have been kept until all code relying
  on them is updated, but they are now simple wrappers around the
  closest matching standard abstract operation.
- Simplifications of the storage layer: functions that write values to
  storage are now prefixed with 'storage_' to make their purpose clear,
  and as they are not part of the spec they should not contain any steps
  specified by it. Much functionality is now covered by the layers above
  it and was removed (e.g. handling of accessors, attribute checks).
- PropertyAttributes has been greatly simplified, and is being replaced
  by PropertyDescriptor - a concept similar to the current
  implementation, but more aligned with the actual spec. See the commit
  message of the previous commit where it was introduced for details.
- As a bonus, and since I had to look at the spec a whole lot anyway, I
  introduced more inline comments with the exact steps from the spec -
  this makes it super easy to verify correctness.
- East-const all the things.

As a result of all of this, things are much more correct but a bit
slower now. Retaining speed wasn't a consideration at all, I have done
no profiling of the new code - there might be low hanging fruits, which
we can then harvest separately.

Special thanks to Idan for helping me with this by tracking down bugs,
updating everything outside of LibJS to work with these changes (LibWeb,
Spreadsheet, HackStudio), as well as providing countless patches to fix
regressions I introduced - there still are very few (we got it down to
5), but we also get many new passing test262 tests in return. :^)

Co-authored-by: Idan Horowitz <idan.horowitz@gmail.com>
2021-07-04 22:07:36 +01:00
Idan Horowitz
e480d69130 LibJS: Bring ArrayCreate and ArrayConstructor closer to spec
Specifically, this now explicitly takes the length, adds missing
exceptions checks to calls with user-supplied lengths, takes and uses
the prototype argument, and fixes some spec non-conformance in
ArrayConstructor and its native functions around the use of ArrayCreate
2021-07-04 00:51:43 +01:00
Andreas Kling
c52ea3dad5 LibJS: Try to fix Clang build (NewClass::m_class_expression is unused) 2021-07-01 17:46:45 +02:00
Johan Dahlin
f6028c2534 LibJS: NewClass bytecode instruction
This adds a the NewClass bytecode instruction, enough of it
is implemented for it to show it in the bytecode (js -d).
2021-07-01 17:34:05 +02:00
Andreas Kling
44221756ab LibJS: Drop "Record" suffix from all the *Environment record classes
"Records" in the spec are basically C++ classes, so let's drop this
mouthful of a suffix.
2021-07-01 12:28:57 +02:00
Idan Horowitz
005d75656e LibCrypto: Replace from_base{2,8,10,16}() & to_base10 with from_base(N)
This allows us to support parsing and serializing BigIntegers to and
from any base N (such that 2 <= N <= 36).
2021-06-29 16:55:54 +01:00
Andreas Kling
c8270dbe2e LibJS: Rename ScriptFunction => OrdinaryFunctionObject
These are basically what the spec calls "ordinary function objects",
so let's have the name reflect that. :^)
2021-06-27 22:36:04 +02:00
Andreas Kling
c2ad599783 LibJS: Rename CallFrame => ExecutionContext
This struct represents what the ECMAScript specification calls an
"execution context" so let's use the same terminology. :^)
2021-06-24 19:28:00 +02:00
Andreas Kling
8a3c9d9851 LibJS: Remove direct argument loading since it was buggy
The parser doesn't always track lexical scopes correctly, so let's not
rely on that for direct argument loading.

This reverts the LoadArguments bytecode instruction as well. We can
bring these things back when the parser can reliably tell us that
a given Identifier is indeed a function argument.
2021-06-22 22:20:17 +02:00
Andreas Kling
1d20380859 LibJS: Split the per-call-frame environment into lexical and variable
To better follow the spec, we need to distinguish between the current
execution context's lexical environment and variable environment.

This patch moves us to having two record pointers, although both of
them point at the same environment records for now.
2021-06-22 18:44:53 +02:00
Andreas Kling
08510a0c80 LibJS: Rename VM::current_scope() => current_environment_record()
And rename some related functions that wrapped this as well.
2021-06-21 23:49:50 +02:00
Andreas Kling
6c6dbcfc36 LibJS: Rename Environment Records so they match the spec :^)
This patch makes the following name changes:

- ScopeObject => EnvironmentRecord
- LexicalEnvironment => DeclarativeEnvironmentRecord
- WithScope => ObjectEnvironmentRecord
2021-06-21 23:49:50 +02:00
Matthew Olsson
df65ff8a1e LibJS: Add bytecode support for regexp literals 2021-06-21 00:28:07 +02:00
Matthew Olsson
25baefdd1e LibJS: Support object rest elements in the bytecode interpreter 2021-06-19 09:38:26 +02:00
Matthew Olsson
57b9a228ab LibJS: Support array rest elements in the bytecode interpreter 2021-06-19 09:38:26 +02:00
Matthew Olsson
7983324639 LibJS: Implement array destructuring for the bytecode interpreter 2021-06-19 09:38:26 +02:00
Matthew Olsson
f39ab2e60a LibJS: Add JumpUndefined bytecode 2021-06-19 09:38:26 +02:00
Matthew Olsson
3ee627909a LibJS: Ensure GetBy{Id,Value} never load <empty> into the accumulator 2021-06-19 09:38:26 +02:00
Linus Groh
317b88a8c3 LibJS: Replace Object's create_empty() with create() taking a prototype
This now matches the spec's OrdinaryObjectCreate() across the board:
instead of implicitly setting the created object's prototype to
%Object.prototype% and then in many cases setting it to a nullptr right
away, it now has an 'Object* prototype' parameter with _no default
value_. This makes the code easier to compare with the spec, very clear
in terms of what prototype is being used as well as avoiding unnecessary
shape transitions.

Also fixes a couple of cases were we weren't setting the correct
prototype.

There's no reason to assume that the object would not be empty (as in
having own properties), so let's follow our existing pattern of
Type::create(...) and simply call it 'create'.
2021-06-16 22:49:04 +01:00
Ali Mohammad Pur
1414c7b049 LibJS: Add a basic pass manager and add some basic passes
This commit adds a bunch of passes, the most interesting of which is a
pass that merges blocks together, and a pass that places blocks that
flow into each other next to each other, and a very simply pass that
removes duplicate basic blocks.
Note that this does not remove the jump at the end of each block in that
pass to avoid scope creep in the passes.
2021-06-15 22:06:33 +04:30
Ali Mohammad Pur
e81fd7106b LibJS: Rename the overridden Instruction methods to foo_impl
These are pretty hairy if someone forgets to override one, as the
catchall function in Instruction will keep calling itself over and over
again, leading to really hard-to-debug situations.
2021-06-15 22:06:33 +04:30
Ali Mohammad Pur
4c7c7c38e2 LibJS: Make EnterUnwindContext a terminator op
Otherwise a basic block could have multiple outgoing edges without
having much reason to do so.
2021-06-15 22:06:33 +04:30
Andreas Kling
91fbeeab72 LibJS: Add LoadArgument bytecode instruction for fast argument access
This is generated for Identifier nodes that represent a function
argument variable. It loads a given argument index from the current
call frame into the accumulator.
2021-06-14 11:26:12 +02:00
Ali Mohammad Pur
8b3f8879c1 LibJS: Use an enum class instead of 'bool is_generator'
This avoid confusion in the order of the multiple boolean parameters
that exist.
2021-06-11 19:42:58 +04:30
Andreas Kling
af48a066c6 LibJS: Add bytecode generation for FunctionExpression :^) 2021-06-11 10:46:46 +02:00
Andreas Kling
9ee5029bc5 LibJS: Basic bytecode support for computed member expressions
Expressions like foo[1 + 2] now work, and you can assign to them
as well! :^)
2021-06-11 00:36:18 +02:00
Ali Mohammad Pur
3234697eca LibJS: Implement generator functions (only in bytecode mode) 2021-06-11 00:30:09 +02:00