Commit graph

1458 commits

Author SHA1 Message Date
Linus Groh
01fd6ce045 LibJS: Support multiple arguments in Array constructor 2020-05-08 20:06:49 +02:00
Linus Groh
8137f40b73 LibJS: Add Value::is_integer() 2020-05-08 20:06:49 +02:00
Matthew Olsson
532d4bc0ab LibJS: Spec-compliant equality comparisons
The ECMAScript spec defines multiple equality operations which are used
all over the spec; this patch introduces them. Of course, the two
primary equality operations are AbtractEquals ('==') and StrictEquals
('==='), which have been renamed to 'abstract_eq' and 'strict_eq' in
this patch.

In support of the two operations mentioned above, the following have
also been added: SameValue, SameValueZero, and SameValueNonNumeric.
These are important to have, because they are used elsewhere in the spec
aside from the two primary equality comparisons.
2020-05-08 09:49:20 +02:00
Yonatan Goldschmidt
b184f12aaf LibJS: Limit scope of 'for' loop variables
This required 2 changes:
1. In the parser, create a new variable scope, so the variable is
   declared in it instead of the scope in which the 'for' is found.
2. On execute, push the variable into the newly created block. Existing
   code created an empty block (no variables, no arguments) which
   allows Interpreter::enter_scope() to skip the creation of a new
   environment, therefore when the variable initializer is executed, it
   sets the variable to the outer scope. By attaching the variable to
   the new block, the block gets a new environment.

This is only needed for 'let' / 'const' declarations, since 'var'
declarations are expected to leak.

Fixes: #2103
2020-05-07 23:31:49 +02:00
Matthew Olsson
ab652fa1ee LibJS: Add String.raw 2020-05-07 23:05:55 +02:00
Matthew Olsson
b5f1df57ed LibJS: Add raw strings to tagged template literals
When calling a function with a tagged template, the first array that is
passed in now contains a "raw" property with the raw, escaped strings.
2020-05-07 23:05:55 +02:00
Emanuele Torre
6bbd0a18a1 LibJS: Fix shellcheck warnings in Tests/run-tests 2020-05-07 12:20:41 +02:00
Matthew Olsson
107ca2e4ba LibJS: Add function call spreading
Adds support for the following syntax:

    myFunction(...x, ...[1, 2, 3], ...o.foo, ...'abcd')
2020-05-06 19:19:02 +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
Andreas Kling
025cdfdce8 LibJS: Simplify a Value type check in Object::to_string() 2020-05-06 16:40:08 +02:00
Linus Groh
4d20cf57db LibJS: Implement tagged template literals (foobar)
To make processing tagged template literals easier, template literals
will now add one empty StringLiteral before and after each template
expression *if* there's no other string - e.g.:

`${foo}` -> "", foo, ""
`test${foo}${bar}test` -> "test", foo, "", bar, "test"

This also matches the behaviour of many other parsers.
2020-05-06 14:49:53 +02:00
Linus Groh
eea62dd365 LibJS: Add Value::{is, as}_function() 2020-05-06 14:49:53 +02:00
Matthew Olsson
419bce6915 LibJS: Fix syntax error for arrow function non-decl variable assignment
A regression was introduced in dc9b4da where the parser would
incorrectly parse the assignment of arrow functions to (non-declaration)
variables. For example, consider:

    a = () => {}

Because the parser was aware of default parameters, in
try_parse_arrow_function, the equals sign would be interpreted as a
default argument, leading to incorrect parsing of the overall
expression. Also resulted in some funny behavior
(a = () => {} => {} worked just fine!).

The simple fix is to only look for default parameters if the arrow
function is required to have parenthesis.
2020-05-06 12:21:29 +02:00
Andreas Kling
fc5d0a1bd2 LibJS: Switch objects to unique shape after 100 property additions
At that point, it seems unlikely that the shape is gonna be shared with
other objects, and we avoid getting stuck holding a big bag of shapes.
2020-05-05 18:49:45 +02:00
Linus Groh
72d2bd56ce LibJS: Implement modulo assignment operator (%=) 2020-05-05 11:12:27 +02:00
Linus Groh
a2e1f1a872 LibJS: Implement exponentiation assignment operator (**=) 2020-05-05 11:12:27 +02:00
Linus Groh
3e754a15d4 LibJS: Implement bitwise assignment operators (&=, |=, ^=) 2020-05-05 11:12:27 +02:00
Linus Groh
8e4301dea6 LibJS: Add test for assignment operators 2020-05-05 11:12:27 +02:00
Emanuele Torre
e91ab0cb02 LibJS: Implement ConsoleClient
Now, you can optionally specify a ConsoleClient, to customise the
behaviour of the LibJS Console.

To customise the console, create a new ConsoleClient class that inherits
from JS::ConsoleClient and override all the abstract methods.

When Console::log() is called, if Console has a ConsoleClient,
ConsoleClient::log() is called instead.

These abstract methods are Value(void) functions: you can return a Value
which will be returned by the JavaScript function which calls that
method, in JavaScript.
2020-05-05 09:15:16 +02:00
Emanuele Torre
bc7ed4524e LibJS: Add some helpers and use them to re-implement Console functions
Also add const overloads for some getters.

Also const-qualify Interpreter::join_arguments().
2020-05-05 09:15:16 +02:00
Emanuele Torre
8bd9f7e50e LibJS: run clang-format on all the files 2020-05-05 09:15:16 +02:00
Emanuele Torre
30519c22f6 LibJS: Re-implement console functions as wrappers around Console methods
Console methods are now Value(void) functions.

JavaScript functions in the JavaScript ConsoleObject are now implemented
as simple wrappers around Console methods.

This will make it possible for LibJS users to easily override the
default behaviour of JS console functions (even their return value!)
once we add a way to override Console behaviour.
2020-05-05 09:15:16 +02:00
Emanuele Torre
73bead5ae9 LibJS: Move join_args() in Interpreter
It can be useful outside of Runtime/ConsoleObject.cpp.
join_args() => Interpreter::join_arguments()
2020-05-05 09:15:16 +02:00
Emanuele Torre
046f9cf115 LibJS: Remove ConsoleMessage from LibJS
We don't need to store the past messages in LibJS.
We'll implement a way to let LibJS users expand the vanilla Console.
2020-05-05 09:15:16 +02:00
Linus Groh
454c1e6bbe LibJS: Implement rest parameters 2020-05-04 23:30:52 +02:00
Linus Groh
d4bfcea570 LibJS: Add indentation to sections in SwitchCase::dump()
This now matches the output of

Program
  (Variables)
    ...
  (Children)
    ...

or

FunctionDeclaration 'foo'
  (Parameters)
    ...
  (Body)
    ...

etc.

Also don't print each consequent statement index, it doesn't add any
value.
2020-05-04 21:42:47 +02:00
mattco98
adb4accab3 LibJS: Add template literals
Adds fully functioning template literals. Because template literals
contain expressions, most of the work has to be done in the Lexer rather
than the Parser. And because of the complexity of template literals
(expressions, nesting, escapes, etc), the Lexer needs to have some
template-related state.

When entering a new template literal, a TemplateLiteralStart token is
emitted. When inside a literal, all text will be parsed up until a '${'
or '`' (or EOF, but that's a syntax error) is seen, and then a
TemplateLiteralExprStart token is emitted. At this point, the Lexer
proceeds as normal, however it keeps track of the number of opening
and closing curly braces it has seen in order to determine the close
of the expression. Once it finds a matching curly brace for the '${',
a TemplateLiteralExprEnd token is emitted and the state is updated
accordingly.

When the Lexer is inside of a template literal, but not an expression,
and sees a '`', this must be the closing grave: a TemplateLiteralEnd
token is emitted.

The state required to correctly parse template strings consists of a
vector (for nesting) of two pieces of information: whether or not we
are in a template expression (as opposed to a template string); and
the count of the number of unmatched open curly braces we have seen
(only applicable if the Lexer is currently in a template expression).

TODO: Add support for template literal newlines in the JS REPL (this will
cause a syntax error currently):

    > `foo
    > bar`
    'foo
    bar'
2020-05-04 16:46:31 +02:00
Linus Groh
32742709dc LibJS: Support empty statements
We already skipped random semicolons in Parser::parse_program(), but now
they are properly matched and parsed as empty statements - and thus
recognized as a valid body of an if / else / while / ... statement.
2020-05-03 12:57:48 +02:00
Linus Groh
25cf0da2fb LibJS: Set name of anonymous functions during assignment 2020-05-03 11:41:56 +02:00
Matthew Olsson
5e66f1900b LibJS: Add function default arguments
Adds the ability for function arguments to have default values. This
works for standard functions as well as arrow functions. Default values
are not printed in a <function>.toString() call, as nodes cannot print
their source string representation.
2020-05-03 00:44:57 +02:00
Andreas Kling
c00ff4ba62 LibJS: Fix build (GlobalObject::add_constructor not visible in LibWeb) 2020-05-02 20:43:44 +02:00
Linus Groh
99be27b4a1 LibJS: Add "name" property to functions 2020-05-02 20:41:31 +02:00
Linus Groh
d007e8d00f LibJS: Set correct "length" of Object constructor 2020-05-02 20:41:31 +02:00
Linus Groh
85582953c6 LibJS: Minor formatting changes in Function.cpp 2020-05-02 20:41:31 +02:00
Linus Groh
ae05dc8abc LibJS: Name functions created by "Function" "anonymous"
...as it is supposed to be.
2020-05-02 20:41:31 +02:00
Emanuele Torre
be1a5bf3f7 LibJS: Add ConsoleMessage concept
A ConsoleMessage is a struct cointaining:
 * AK::String text;         represents the text of the message sent
                             to the console.
 * ConsoleMessageKind kind; represents the kind of JS `console` function
                             from which the message was sent.

Now, Javascript `console` functions only send a ConsoleMessage to the
Interpreter's Console instead of printing text directly to stdout.
The Console then stores the recived ConsoleMessage in
Console::m_messages; the Console does not print to stdout by default.

You can set Console::on_new_message to a void(ConsoleMessage&); this
function will get call everytime a new message is added to the Console's
messages and can be used, for example, to print ConsoleMessages to
stdout or to color the output based on the kind of ConsoleMessage.

In this patch, I also:
  * Re-implement all the previously implemented functions in the
     JavaScript ConsoleObject, as wrappers around Console functions
     that add new message to the Console.
  * Implement console.clear() like so:
    - m_messages get cleared;
    - a new_message with kind set ConsoleMessageKind::Clear gets added
       to m_messages, its text is an empty AK::String;
  * Give credit to linusg in Console.cpp since I used his
     console.trace() algorithm in Console::trace().

I think that having this abstration will help us in the implementation
of a browser console or a JS debugger. We could also add more MetaData
to ConsoleMessage, e.g. Object IDs of the arguments passed to console
functions in order to make hyperlinks, Timestamps, ecc.; which could be
interesting to see.

This will also help in implementing a `/bin/js` option to make, for
example, return a ConsoleMessageWrapper to console functions instead of
undefined. This will be useful to make tests for functions like
console.count() and console.countClear(). :^)
2020-05-02 11:41:35 +02:00
Emanuele Torre
e9c7d4524a LibJS: Implement ConsoleObject::count() as a Console::count() wrapper
Also implement ConsoleObject::count_clear() as a wrapper for
Console::count_clear()
2020-05-02 11:41:35 +02:00
Emanuele Torre
2e92c2e5e1 LibJS: Start implementing a Console class for the interpreter
The goal is to start factoring out core ConsoleObject functionality and
to make ConsoleObject only a JS wrapper around Console.
2020-05-02 11:41:35 +02:00
Linus Groh
43c1fa9965 LibJS: Implement (no-op) debugger statement 2020-05-01 22:07:13 +02:00
Linus Groh
79b829637e LibJS: Implement most of the Reflect object 2020-05-01 16:54:01 +02:00
Linus Groh
1ba2e6768d LibJS: Implement indexed access for StringObject 2020-05-01 16:54:01 +02:00
Linus Groh
a81bce8c2a LibJS: Make Array.length non-configurable
This was incorrect, it's only writable.
2020-05-01 16:54:01 +02:00
Linus Groh
65dbe17dd7 LibJS: Add Value::to_size_t() 2020-05-01 16:54:01 +02:00
Linus Groh
62671bea68 LibJS: Add Object::has_property()
Like Object::has_own_property() but going down the prototype chain.
2020-05-01 16:54:01 +02:00
Linus Groh
4cdd802927 LibJS: Return a bool from Object::put* to indicate success 2020-05-01 16:54:01 +02:00
Kesse Jones
6dbb5df81f LibJS: Add String.prototype.lastIndexOf 2020-05-01 16:50:37 +02:00
Emanuele Torre
46b79eaad9 LibJS: Implement console.countReset()
I chose to also make it print "<counter_name>: 0\n" when a counter gets
reset, similarly to how firefox behaves.
2020-05-01 13:02:01 +02:00
Emanuele Torre
8c60ba1e42 LibJS: Implement console.count() 2020-05-01 13:02:01 +02:00
Matthew Olsson
28ef654d13 LibJS: Add object literal method shorthand 2020-05-01 12:28:40 +02:00
mattco98
683a0696f3 LibJS: Add Object.{keys,values,entries}() 2020-04-30 09:53:16 +02:00