Commit graph

72 commits

Author SHA1 Message Date
Andreas Kling
13d7c09125 Libraries: Move to Userland/Libraries/ 2021-01-12 12:17:46 +01:00
Lenny Maiorani
e6f907a155 AK: Simplify constructors and conversions from nullptr_t
Problem:
- Many constructors are defined as `{}` rather than using the ` =
  default` compiler-provided constructor.
- Some types provide an implicit conversion operator from `nullptr_t`
  instead of requiring the caller to default construct. This violates
  the C++ Core Guidelines suggestion to declare single-argument
  constructors explicit
  (https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c46-by-default-declare-single-argument-constructors-explicit).

Solution:
- Change default constructors to use the compiler-provided default
  constructor.
- Remove implicit conversion operators from `nullptr_t` and change
  usage to enforce type consistency without conversion.
2021-01-12 09:11:45 +01:00
Andreas Kling
db790dda62 LibJS: Remove hand-rolled type information in JS AST in favor of RTTI 2021-01-01 19:34:07 +01:00
Andreas Kling
f48751a739 LibJS: Remove hand-rolled Object is_foo() helpers in favor of RTTI 2021-01-01 17:46:39 +01:00
Andreas Kling
48d2545572 LibJS: Get rid of Argument and ArgumentVector
This was used for a feature where you could pass a vector of arguments
to enter_scope(). Since that way of passing arguments was not GC-aware
(as vectors use C++ heap storage), let's avoid using it and make sure
everything that needs to stay alive is either on the stack or in traced
storage instead.
2020-12-08 18:28:18 +01:00
Andreas Kling
fc9e43728b LibJS: Stop creating a redundant lexical scope on function call
We were scoping the arguments twice, first in execute_function_body(),
and then again in enter_scope().
2020-12-08 18:04:54 +01:00
Andreas Kling
2e4832c3da LibJS: Constructor function's "prototype" property should be writable
This matches other engines.
2020-11-29 19:49:27 +01:00
Andreas Kling
c3fe9b4df8 LibJS: Add a scope object abstraction
Both GlobalObject and LexicalEnvironment now inherit from ScopeObject,
and the VM's call frames point to a ScopeObject chain rather than just
a LexicalEnvironment chain.

This gives us much more flexibility to implement things like "with",
and also unifies some of the code paths that previously required
special handling of the global object.

There's a bunch of more cleanup that can be done in the wake of this
change, and there might be some oversights in the handling of the
"super" keyword, but this generally seems like a good architectural
improvement. :^)
2020-11-28 17:16:48 +01:00
Andreas Kling
98f2da9834 LibJS: Rename Cell::visit_children() => Cell::visit_edges()
The GC heap is really a graph of cells, so "children" didn't quite feel
appropriate here.
2020-11-28 17:16:48 +01:00
Linus Groh
1b0c862f3a LibJS: Throw TypeError when calling class constructor without 'new' 2020-11-12 10:14:00 +01:00
Linus Groh
b07c7f589f LibJS: Refactor ScriptFunction::call() a bit
- Get VM reference once
- Less nesting
- Better variable names
2020-11-12 10:14:00 +01:00
Linus Groh
39a1c9d827 LibJS: Implement 'new.target'
This adds a new MetaProperty AST node which will be used for
'new.target' and 'import.meta' meta properties. The parser now
distinguishes between "in function context" and "in arrow function
context" (which is required for this).
When encountering TokenType::New we will attempt to parse it as meta
property and resort to regular new expression parsing if that fails,
much like the parsing of labelled statements.
2020-11-02 22:40:59 +01: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
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
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
Linus Groh
f9eaac62d9 LibJS: Use String::formatted() for throw_exception() message 2020-10-04 19:22:02 +02:00
Andreas Kling
a007b3c379 LibJS: Move "strict mode" state to the call stack
Each call frame now knows whether it's executing in strict mode.
It's no longer necessary to access the scope stack to find this mode.
2020-10-04 17:03:33 +02:00
Matthew Olsson
6eb6752c4c LibJS: Strict mode is now handled by Functions and Programs, not Blocks
Since blocks can't be strict by themselves, it makes no sense for them
to store whether or not they are strict. Strict-ness is now stored in
the Program and FunctionNode ASTNodes. Fixes issue #3641
2020-10-04 10:46:12 +02:00
Andreas Kling
3df604ad12 LibJS: Reduce use of Interpreter in LexicalEnvironment 2020-09-29 16:41:28 +02:00
Andreas Kling
063acda76e LibJS: Remove a bunch of unnecessary uses of Cell::interpreter()
We'll want to get rid of all uses of this, to free up the engine from
the old assumption that there's always an Interpreter available.
2020-09-27 20:26:58 +02:00
Andreas Kling
f79d4c7347 LibJS: Remove Interpreter& argument to Function::construct()
This is no longer needed, we can get everything we need from the VM.
2020-09-27 20:26:58 +02:00
Andreas Kling
340a115dfe LibJS: Make native function/property callbacks take VM, not Interpreter
More work on decoupling the general runtime from Interpreter. The goal
is becoming clearer. Interpreter should be one possible way to execute
code inside a VM. In the future we might have other ways :^)
2020-09-27 20:26:58 +02:00
Andreas Kling
1ff9d33131 LibJS: Make Function::call() not require an Interpreter&
This makes a difference inside ScriptFunction::call(), which will now
instantiate a temporary Interpreter if one is not attached to the VM.
2020-09-27 20:26:58 +02:00
Andreas Kling
be31805e8b LibJS: Move scope stack from VM back to Interpreter
Okay, my vision here is improving. Interpreter should be a thing that
executes an AST. The scope stack is irrelevant to the VM proper,
so we can move that to the Interpreter. Same with execute_statement().
2020-09-27 20:26:58 +02:00
Andreas Kling
6861c619c6 LibJS: Move most of Interpreter into VM
This patch moves the exception state, call stack and scope stack from
Interpreter to VM. I'm doing this to help myself discover what the
split between Interpreter and VM should be, by shuffling things around
and seeing what falls where.

With these changes, we no longer have a persistent lexical environment
for the current global object on the Interpreter's call stack. Instead,
we push/pop that environment on Interpreter::run() enter/exit.
Since it should only be used to find the global "this", and not for
variable storage (that goes directly into the global object instead!),
I had to insert some short-circuiting when walking the environment
parent chain during variable lookup.

Note that this is a "stepping stone" commit, not a final design.
2020-09-27 20:26:58 +02:00
Linus Groh
ec43f73b74 LibJS: Extract most of Interpreter's run() into execute_statement()
Interpreter::run() was so far being used both as the "public API entry
point" for running a JS::Program as well as internally to execute
JS::Statement|s of all kinds - this is now more distinctly separated.
A program as returned by the parser is still going through run(), which
is responsible for creating the initial global call frame, but all other
statements are executed via execute_statement() directly.

Fixes #3437, a regression introduced by adding ASSERT(!exception()) to
run() without considering the effects that would have on internal usage.
2020-09-12 09:31:16 +02:00
Linus Groh
9ea6ef4ed1 LibJS: Make Interpreter::throw_exception() a void function
The motivation for this change is twofold:

- Returning a JS::Value is misleading as one would expect it to carry
  some meaningful information, like maybe the error object that's being
  created, but in fact it is always empty. Supposedly to serve as a
  shortcut for the common case of "throw and return empty value", but
  that's just leading us to my second point.
- Inconsistent usage / coding style: as of this commit there are 114
  uses of throw_exception() discarding its return value and 55 uses
  directly returning the call result (in LibJS, not counting LibWeb);
  with the first style often having a more explicit empty value (or
  nullptr in some cases) return anyway.
  One more line to always make the return value obvious is should be
  worth it.

So now it's basically always these steps, which is already being used in
the majority of cases (as outlined above):

- Throw an exception. This mutates interpreter state by updating
  m_exception and unwinding, but doesn't return anything.
- Let the caller explicitly return an empty value, nullptr or anything
  else itself.
2020-08-25 18:30:31 +02:00
Andreas Kling
aaf6014ae1 LibJS: Simplify Cell::initialize()
Remove the Interpreter& argument and pass only GlobalObject&. We can
find everything we need via the global object anyway.
2020-07-23 17:31:08 +02:00
Matthew Olsson
bda39ef7ab LibJS: Explicitly pass a "Function& new_target" to Function::construct
This allows the proxy handler to pass the proper new.target to construct
handlers.
2020-07-01 11:16:37 +02:00
Jack Karamanian
7533fd8b02 LibJS: Initial class implementation; allow super expressions in object
literal methods; add EnvrionmentRecord fields and methods to
LexicalEnvironment

Adding EnvrionmentRecord's fields and methods lets us throw an exception
when |this| is not initialized, which occurs when the super constructor
in a derived class has not yet been called, or when |this| has already
been initialized (the super constructor was already called).
2020-06-29 17:54:54 +02:00
Jack Karamanian
949bffdc93 LibJS: Define the "constructor" property on ScriptFunction's prototype
and set it to the current function
2020-06-29 17:54:54 +02:00
Andreas Kling
2fe4285693 LibJS: Object::initialize() overrides must always call base class 2020-06-20 17:50:48 +02:00
Andreas Kling
06e29fac57 LibJS: Split more native object constructors into construct/initialize 2020-06-20 17:50:48 +02:00
Andreas Kling
8d56e6103e LibJS: Make Value::to_object() take a GlobalObject& 2020-06-20 17:50:48 +02:00
Andreas Kling
cd14ebb11f LibJS: More Interpreter::global_object() removal
Also let's settle on calling the operation of fetching the "this" value
from the Interpreter and converting it to a specific Object pointer
typed_this() since consistency is nice.
2020-06-20 17:50:48 +02:00
Andreas Kling
64513f3c23 LibJS: Move native objects towards two-pass construction
To make sure that everything is set up correctly in objects before we
start adding properties to them, we split cell allocation into 3 steps:

1. Allocate a cell of appropriate size from the Heap
2. Call the C++ constructor on the cell
3. Call initialize() on the constructed object

The job of initialize() is to define all the initial properties.
Doing it in a second pass guarantees that the Object has a valid Shape
and can find its own GlobalObject.
2020-06-20 15:46:30 +02:00
Andreas Kling
e4add19915 LibJS: Pass GlobalObject& to native functions and property accessors
More work towards supporting multiple global objects. Native C++ code
now get a GlobalObject& and don't have to ask the Interpreter for it.

I've added macros for declaring and defining native callbacks since
this was pretty tedious and this makes it easier next time we want to
change any of these signatures.
2020-06-20 15:45:07 +02:00
Matthew Olsson
78155a6668 LibJS: Consolidate error messages into ErrorTypes.h
Now, exceptions can be thrown with
interpreter.throw_exception<T>(ErrorType:TYPE, "format", "args",
"here").
2020-06-11 07:46:20 +02:00
Andreas Kling
5042e560ef LibJS: Make more Interpreter functions take a GlobalObject& 2020-06-08 21:25:16 +02:00
Andreas Kling
053863f35e LibJS: Interpreter::this_value() => this_value(GlobalObject&)
Once the Interpreter has no global object attached to it, we have to
provide it everywhere.
2020-06-08 21:12:20 +02:00
Andreas Kling
25f2a29d84 LibJS: Pass GlobalObject& to AST node execute() functions
More work towards supporting multiple global objects.
2020-06-08 21:12:20 +02:00
Andreas Kling
affc479e83 LibJS+LibWeb: Remove a bunch of calls to Interpreter::global_object()
Objects should get the GlobalObject from themselves instead. However,
it's not yet available during construction so this only switches code
that happens after construction.

To support multiple global objects, Interpreter needs to stop holding
on to "the" global object and let each object graph own their global.
2020-06-08 12:25:45 +02:00
Jack Karamanian
3ffb0a4e87 LibJS: Throw a TypeError when an arrow function is used as a constructor 2020-05-30 10:33:24 +02:00
Jack Karamanian
1110b1b444 LibJS: Don't define the "prototype" property for arrow functions 2020-05-30 10:33:24 +02:00
Jack Karamanian
45ccd9f8d9 LibJS: Set the bound |this| value to the |this| value of the current
scope for arrow functions
2020-05-30 10:33:24 +02:00
Jack Karamanian
c12125fa81 LibJS: Track whether ScriptFunctions and FunctionExpressions are arrow
functions
2020-05-30 10:33:24 +02:00
Matthew Olsson
5ae9419a06 LibJS: Object index properties have descriptors; Handle sparse indices
This patch adds an IndexedProperties object for storing indexed
properties within an Object. This accomplishes two goals: indexed
properties now have an associated descriptor, and objects now gracefully
handle sparse properties.

The IndexedProperties class is a wrapper around two other classes, one
for simple indexed properties storage, and one for general indexed
property storage. Simple indexed property storage is the common-case,
and is simply a vector of properties which all have attributes of
default_attributes (writable, enumerable, and configurable).

General indexed property storage is for a collection of indexed
properties where EITHER one or more properties have attributes other
than default_attributes OR there is a property with a large index (in
particular, large is '200' or higher).

Indexed properties are now treated relatively the same as storage within
the various Object methods. Additionally, there is a custom iterator
class for IndexedProperties which makes iteration easy. The iterator
skips empty values by default, but can be configured otherwise.
Likewise, it evaluates getters by default, but can be set not to.
2020-05-28 17:17:13 +02:00
Matthew Olsson
dd08c992e8 LibJS: Simplify and normalize publicly-exposed Object functions
Previously, the Object class had many different types of functions for
each action. For example: get_by_index, get(PropertyName),
get(FlyString). This is a bit verbose, so these methods have been
shortened to simply use the PropertyName structure. The methods then
internally call _by_index if necessary. Note that the _by_index
have been made private to enforce this change.

Secondly, a clear distinction has been made between "putting" and
"defining" an object property. "Putting" should mean modifying a
(potentially) already existing property. This is akin to doing "a.b =
'foo'".

This implies two things about put operations:
    - They will search the prototype chain for setters and call them, if
      necessary.
    - If no property exists with a particular key, the put operation
      should create a new property with the default attributes
      (configurable, writable, and enumerable).

In contrast, "defining" a property should completely overwrite any
existing value without calling setters (if that property is
configurable, of course).

Thus, all of the many JS objects have had any "put" calls changed to
"define_property" calls. Additionally, "put_native_function" and
"put_native_property" have had their "put" replaced with "define".

Finally, "put_own_property" has been made private, as all necessary
functionality should be exposed with the put and define_property
methods.
2020-05-27 13:17:35 +02:00
Linus Groh
1a1394f7a2 LibJS: Change Value::to_object(Heap& -> Interpreter&)
Passing a Heap& to it only to then call interpreter() on that is weird.
Let's just give it the Interpreter& directly, like some of the other
to_something() functions.
2020-05-18 09:39:55 +02:00
Matthew Olsson
838390171c LibJS: Function.length respects default and rest parameters
"[Function.length is] the number of formal parameters. This number
excludes the rest parameter and only includes parameters before
the first one with a default value." - MDN
2020-05-06 17:40:56 +02:00