Commit graph

510 commits

Author SHA1 Message Date
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
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
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
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
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
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
Nico Weber
ef1b21004f Everywhere: Fix typos
Mostly in comments, but sprintf() now prints "August" instead of
"Auguest" so that's something.
2020-10-02 16:03:17 +02:00
Linus Groh
c0e4353bde LibJS: Handle getter exception in JSONObject::serialize_json_property()
In the case of an exception in a property getter function we would not
return early, and a subsequent attempt to call the replacer function
would crash the interpreter due to call_internal() asserting.

Fixes #3548.
2020-09-19 14:17:22 +02:00
AnotherTest
21f513fe0f LibJS: Do not revisit already visited values in update_function_name()
Fixes #3471, adds a test.
2020-09-19 00:33:56 +02:00
Linus Groh
a9f5b0339d LibJS: Simplify toEval() implementation 2020-09-18 20:49:35 +02:00
Linus Groh
568d53c9b1 LibJS: Check validity of computed_property_name() result before using it
This fixes two cases obj[expr] and obj[expr]() (MemberExpression and
CallExpression respectively) when expr throws an exception and results
in an empty value, causing a crash by passing the invalid PropertyName
created by computed_property_name() to Object::get() without checking it
first.

Fixes #3459.
2020-09-12 11:29:39 +02:00
Linus Groh
75dac35d0e LibJS: Stop unwinding and reset exception for TryStatement finalizer
This fixes two issues with running a TryStatement finalizer:

- Temporarily store and clear the exception, if any, so we can run the
  finalizer block statement without it getting in our way, which could
  have unexpected side effects otherwise (and will likely return early
  somewhere).
- Stop unwinding so more than one child node of the finalizer
  BlockStatement is executed if an exception has been thrown previously
  (which would have called unwind(ScopeType::Try)). Re-throwing as
  described above ensures we still unwind after the finalizer, if
  necessary.

Also add some tests specifically for try/catch/finally blocks, we
didn't have any!
2020-09-12 09:31:16 +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
55c4866370 LibJS: Add tests for issue #3382 2020-09-01 21:35:59 +02:00
AnotherTest
8e89233bba LibJS: Demonstrate weird behaviour with 'break' 2020-08-28 20:19:56 +02:00
Nico Weber
2c1b84b3e1 LibJS: Add some more tests, mostly around leap years 2020-08-26 08:52:07 +02:00
Nico Weber
e4dac38127 JS Tests: Disable the one failing test when running test-js in Serenity 2020-08-25 21:23:10 +02:00
AnotherTest
54036d660a Meta: Move prettier config files to the root of the repository 2020-08-24 18:21:33 +02:00
Nico Weber
2191ec591f LibJS: Make Date's tuple constructor correctly handle out-of-range arguments
Milliseconds need extra handling, but everything else just works
now that mktime() handles this case.
2020-08-24 18:20:07 +02:00
Nico Weber
84f729c2b4 LibJS+LibC: Add tests for Date tuple ctor overflow and make mktime()/timegm() handle month overflow 2020-08-24 09:30:11 +02:00
Nico Weber
ad00462daa LibJS: Implement Date.getUTC*
Test files created with:
    $ for f in Libraries/LibJS/Tests/builtins/Date/Date.prototype.get*js; do
          cp $f $(echo $f | sed -e 's/get/getUTC/') ;
      done
    $ rm Libraries/LibJS/Tests/builtins/Date/Date.prototype.getUTCTime.js
    $ git add Libraries/LibJS/Tests/builtins/Date/Date.prototype.getUTC*.js
    $ ls Libraries/LibJS/Tests/builtins/Date/Date.prototype.getUTC*.js | \
          xargs sed -i -e 's/get/getUTC/g'
2020-08-23 22:00:05 +02:00
Nico Weber
5f595e7e1b LibC: Make localtime() and gmtime() handle years before 1970
Year computation has to be based on seconds, not days, in case
t is < 0 but t / __seconds_per_day is 0.

Year computation also has to consider negative timestamps.

With this, days is always positive and <= the number of days in the
year, so base the tm_wday computation directly on the timestamp,
and do it first, before t is modified in the year computation.
In C, % can return a negative number if the left operand is negative,
compensate for that.

Tested via test-js. (Except for tm_wday, since we don't implement
Date.prototype.getUTCDate() yet.)
2020-08-23 10:42:37 +02:00
Nico Weber
cec467fe35 LibJS: Enable Date.parse.js tests that pass after c399caf27f 2020-08-23 10:42:37 +02:00
Nico Weber
c399caf27f LibC: Make mktime() and timegm() handle years before 1970
And also years that don't fit in 32-bit.

Lovingly tested via LibJS's Date.UTC(), which happens to call
timegm().
2020-08-22 10:53:33 +02:00
Nico Weber
96891669c3 test-js: Sometimes include more details for failures
LibJS doesn't store stacks for exception objects, so this
only amends test-common.js's __expect() with an optional
`details` function that can produce a more detailed error
message, and it lets test-js.cpp read and print that
error message.  I added the optional details parameter to
a few matchers, most notably toBe() where it now prints
expected and actual value.

It'd be nice to have line numbers of failures, but that
seems hard to do with the current design, and this is already
much better than the current state.
2020-08-22 10:52:40 +02:00
Nico Weber
ebd510ef5e LibJS: Allow conversion from Symbol to String via explicit String() call
https://tc39.es/ecma262/#sec-string-constructor-string-value has an
explicit special case for Symbols allowing this:

    If NewTarget is undefined and Type(value) is Symbol,
    return SymbolDescriptiveString(value).
2020-08-22 10:52:40 +02:00
Nico Weber
116c0c0ab3 LibJS: Implement Date's string constructor
... by calling Date.parse().

With this, dates on http://45.33.8.238/ and
http://45.33.8.238/linux/summary.html are correctly converted to local
time :^)
2020-08-21 21:12:54 +02:00
Nico Weber
6e5aa5d5df LibJS: Implement Date.parse()
The spec says Date.parse() should accept at least a simplified form
of ISO 8601, so that's all this implements.
2020-08-21 21:12:54 +02:00
Nico Weber
a6b68451dc LibJS: Implement Date.prototype.toISOString() 2020-08-21 12:11:48 +02:00
Nico Weber
1eac1b360b LibJS: Implement Date.UTC() 2020-08-21 12:11:48 +02:00
Nico Weber
d4d9222eea LibJS: Basic implementation of most of Date's constructor arguments
The constructor with a string argument isn't implemented yet, but
this implements the other variants.

The timestamp constructor doens't handle negative timestamps correctly.

Out-of-bound and invalid arguments aren't handled correctly.
2020-08-20 20:53:43 +02:00
Nico Weber
8ebef785eb LibJS: Implement basic functionality of Array.from()
The optional 2nd and 3rd arguments are not yet implemented.

This assumes that `this` is the Array constructor and doesn't yet
implement the more general behavior in the ES6 spec that allows
transferring this method to other constructors.
2020-08-17 21:23:11 +02:00
Nico Weber
79a5ba58a5 LibJS: Add tests for bitwise & and ^
And fix some edge case conversion bugs found by the tests.
2020-07-23 13:06:49 +02:00
Nico Weber
9e32ad6c99 LibJS: Fix \x escapes of bytes with high bit set
With this, typing `"\xff"` into Browser's console no longer
makes the app crash.

While here, also make the \u handler call append_codepoint()
instead of calling an overload where it's not immediately clear
which overload is getting called. This has no behavior change.
2020-07-22 19:21:35 +02:00
Nico Weber
979e02c0a8 LibJS: Implement String.prototype.charCodeAt
It's broken for strings with characters outside 7-bit ASCII, but
it's broken in the same way as several existing functions (e.g.
charAt()), so that's probably ok for now.
2020-07-22 15:48:01 +02:00
Matthew Olsson
02305d01ea LibJS: Add Number.prototype.toString 2020-07-15 18:24:55 +02:00
Matthew Olsson
6075defd55 LibJS: Add Symbol.hasInstance tests 2020-07-14 20:15:19 +02:00
Matthew Olsson
a51b2393f2 LibJS: Integrate iterator protocol into language features
Finally use Symbol.iterator protocol in language features :) currently
only used in for-of loops and spread expressions, but will have more
uses later (Maps, Sets, Array.from, etc).
2020-07-14 17:58:42 +02:00
Matthew Olsson
4c3a415dc3 LibJS: Add String Iterator tests 2020-07-13 15:07:29 +02:00
Matthew Olsson
43d955014d LibJS: Implement Symbol.toStringTag 2020-07-11 23:13:29 +02:00
Matthew Olsson
5ecd504f4e LibJS: Implement spec-compliant Object.prototype.toString 2020-07-11 23:13:29 +02:00
Matthew Olsson
2ea85355fe LibJS: Start implementing iterable framework, add ArrayIterator
With the addition of symbol keys, work can now be done on starting to
implement the well-known symbol functionality. The most important of
these well-known symbols is by far Symbol.iterator.

This patch adds IteratorPrototype, as well as ArrayIterator and
ArrayIteratorPrototype. In the future, sometime after StringIterator has
also been added, this will allow us to use Symbol.iterator directly in
for..of loops, enabling the use of custom iterator objects. Also makes
adding iterator support to native objects much easier (as will have to
be done for Map and Set, when they get added).
2020-07-11 18:54:13 +02:00
Matthew Olsson
119386ffb0 LibJS: Add tests for symbol object integration 2020-07-09 23:33:00 +02:00
Matthew Olsson
9783a4936c LibJS: Add test for well-known symbols 2020-07-09 23:29:28 +02:00
Matthew Olsson
ffb569fd5d LibJS: Uncomment remaining symbol tests 2020-07-09 23:29:28 +02:00
Matthew Olsson
93ebd320ef LibJS: Object.preventExtensions should allow property modfication
Existing properties on a non-extensible object should be changable and
deletable.
2020-07-07 10:47:10 +02:00
Linus Groh
461d90d042 LibJS: Convert Array tests to new testing framework 2020-07-06 23:40:35 +02:00
Linus Groh
8ebdf685a6 LibJS: Split isNaN tests into multiple sections 2020-07-06 23:40:35 +02:00
Matthew Olsson
1ef573eb30 LibJS: Indent tests with 4 spaces instead of 2 2020-07-06 23:40:35 +02:00
Matthew Olsson
15de2eda2b LibJS: Convert all remaining non-Array tests to the new system :) 2020-07-06 23:40:35 +02:00
Matthew Olsson
918f4affd5 LibJS: Convert remaining top-level tests to new system 2020-07-06 23:40:35 +02:00
Matthew Olsson
6d58c48c2f test-js: Use prettier and format all files 2020-07-06 23:40:35 +02:00
Matthew Olsson
a2dbd955f2 test-js: Display messages from console.log in test output
This will help greatly with debugging!
2020-07-06 23:40:35 +02:00
Matthew Olsson
82fa65135a test-js: Allow skipping tests with "test.skip(name, callback)"
Skipped tests count as a "pass" rather than a "fail" (i.e. a test suite
with a skipped test will pass), however it does display a message when
the test is printing.

This is intended for tests which _should_ work, but currently do not.
This should be preferred over "// FIXME" notes if possible.
2020-07-06 23:40:35 +02:00
Matthew Olsson
cf537311e4 test-js: Remove run-tests.sh
The shell script is no longer necessary -- simply run "test-js" from
inside Serenity, or $SERENITY_ROOT/Build/Meta/Lagom/test-js from the
host.
2020-07-06 23:40:35 +02:00
Matthew Olsson
26acc8ba88 LibJS/test-js: Clean up test-js code
This commit also exposes JSONObject's implementation of stringify to the
public, so that it can be used by test-js without having to go through
the interpreter's environment.
2020-07-06 23:40:35 +02:00
Matthew Olsson
3f97d75778 LibJS: Convert most builtin tests to new system 2020-07-06 23:40:35 +02:00
Linus Groh
46581773c1 LibJS: Convert Reflect object tests to new testing framework 2020-07-06 23:40:35 +02:00
Matthew Olsson
fc08222f46 LibJS: Add more test matchers 2020-07-06 23:40:35 +02:00
Matthew Olsson
449a1cf3a2 LibJS: Convert Proxy tests 2020-07-06 23:40:35 +02:00
Matthew Olsson
b86faeaeef LibJS: Add test-common.js tests, remove unused matchers
Now that test-common.js is quite a bit more complicated, we need tests
for test-common to make sure all of the matchers behave correctly
2020-07-06 23:40:35 +02:00
Matthew Olsson
eea6041302 LibJS: Convert some top-level tests to the new system
First test conversions! These look really good :)
2020-07-06 23:40:35 +02:00
Matthew Olsson
4b8a3e6d78 LibJS: Refactor run-tests.sh to use the new test-js program 2020-07-06 23:40:35 +02:00
Matthew Olsson
b9cf7a833f LibJS/test-js: Create test-js program, prepare for test suite refactor
This moves most of the work from run-tests.sh to test-js.cpp. This way,
we have a lot more control over how the test suite runs, as well as how
it outputs. This should result in some cool functionality!

This commit also refactors test-common.js to mimic the jest library.
This should allow tests to be much more expressive :)
2020-07-06 23:40:35 +02:00
Matthew Olsson
6af3fff0c2 LibJS: Reformat run-tests.sh output
- Use emojis instead of the pass/fail text
- Fix the new version of the script to run inside Serenity
- Don't print erroneous output after 'Output:'; start on a newline
instead
- Skip 'run-tests.sh' while testing
2020-07-03 19:30:13 +02:00
Matthew Olsson
4c48c9d69d LibJS: Reorganize tests into subfolders 2020-07-03 19:30:13 +02:00
Matthew Olsson
02debd8a6d LibJS: Remove extra colon in run-tests.sh output 2020-07-03 19:30:13 +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
Matthew Olsson
19411e22d0 LibJS: Add Proxy [[Call]] and [[Construct]] tests 2020-07-01 11:16:37 +02:00
Andreas Kling
ed683663cd LibJS: Skip some Math object tests that fail on Serenity
Mark a bunch of these with FIXME so that someone can find them and
fix them eventually. :^)
2020-06-30 23:11:07 +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
Matthew Olsson
53f1090b86 LibJS: run-test.sh emits test output if it is not "PASS"
Previously, debugging a test with console.log statements was impossible,
because it would just cause the test to fail with no additional output.
Now, if the output of a test is not "PASS", the output will be printed
under the line where the test failed.

Empty output will have a special message attached to it -- useful when
a test author has forgotten to include `console.log("PASS")` at the end
of a test.
2020-06-26 12:40:07 +02:00
Linus Groh
afcfea2001 LibJS: Handle "receiver" argument in Reflect.{get,set}() 2020-06-25 15:51:47 +02:00
stelar7
9e18005c64 LibJS: expose some more math functions 2020-06-22 10:33:50 +02:00
Matthew Olsson
b155e64b67 LibJS: Add JSON.parse 2020-06-13 12:43:22 +02:00
Matthew Olsson
39576b2238 LibJS: Add JSON.stringify 2020-06-13 12:43:22 +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
Linus Groh
0ff9d7e189 LibJS: Add BigInt 2020-06-07 19:29:40 +02:00
Matthew Olsson
39ad42defd LibJS: Add Proxy objects
Includes all traps except the following: [[Call]], [[Construct]],
[[OwnPropertyKeys]].

An important implication of this commit is that any call to any virtual
Object method has the potential to throw an exception. These methods
were not checked in this commit -- a future commit will have to protect
these various method calls throughout the codebase.
2020-06-06 22:13:01 +02:00
Marcin Gasperowicz
0cbef87944 LibJS: Fix rest-params test to take function hoisting into account 2020-06-06 10:53:06 +02:00
Marcin Gasperowicz
2579d0bf55 LibJS: Hoist function declarations
This patch adds function declaration hoisting. The mechanism
is similar to var hoisting. Hoisted function declarations are to be put
before the hoisted var declarations, hence they have to be treated
separately.
2020-06-06 10:53:06 +02:00
Marcin Gasperowicz
0b74ea3d6a LibJS: Make typeof return undefined for undefined variables
This makes `typeof i_dont_exist` return `undefined` instead of 
throwing an error.
2020-06-03 19:31:44 +02:00
Jack Karamanian
b0932b0aec LibJS: Allow null or undefined as a bound |this| value in strict mode 2020-06-03 08:19:03 +02:00
Linus Groh
b32761f2e0 LibJS: Consider non-extensible objects in Reflect.setPrototypeOf() 2020-06-02 13:51:02 +02:00
Linus Groh
c1248a7fd8 LibJS: Implement Reflect.{isExtensible,preventExtensions}() 2020-06-02 13:51:02 +02:00
Linus Groh
b958e4f573 LibJS: Disallow changing the prototype of non-extensible objects
Object::set_prototype() now returns a boolean indicating success.
Setting the prototype to an identical object is always considered
successful, even if the object is non-extensible.
2020-06-02 13:51:02 +02:00
Linus Groh
8cf1ded478 LibJS: Don't assume Object.setPrototypeOf() prototype value is an object
We're crashing otherwise. Also it was not possible to set the prototype
to null.
2020-06-02 13:51:02 +02:00
Linus Groh
1a64bdd80c LibJS: Return specified object from Object.setPrototypeOf()
We were leaking an empty value.
2020-06-02 13:51:02 +02:00
Matthew Olsson
d5ae73a63b LibJS: Add Object.{isExtensible,preventExtensions}() 2020-06-02 08:50:26 +02:00
Matthew Olsson
ab576e610c LibJS: Rewrite Parser.parse_object_expression()
This rewrite drastically increases the accuracy of object literals.
Additionally, an "assertIsSyntaxError" function has been added to
test-common.js to assist in testing syntax errors.
2020-06-01 13:11:21 +02:00
Linus Groh
e33820b557 LibJS: Add String.fromCharCode() 2020-05-31 02:19:52 +02:00
Andreas Kling
29ab518003 LibJS: Show run-tests progress in the taskbar
Use the window progress escape sequence to indicate how far along in
the test collection we are while running tests. :^)
2020-05-30 23:00:35 +02:00
Jack Karamanian
d4e97b17ab LibJS: Use a non-arrow function to check the |this| value in the
callback for Array.prototype.{reduce,reduceRight}

Arrow functions always retain the |this| binding.

Running this code in Node:

[1, 2].reduce(() => { "use strict"; console.log(this === undefined) }

Output: false
2020-05-30 10:33:24 +02:00
Jack Karamanian
4a49c8412c LibJS: Add tests ensuring the |this| value can't be set for arrow
functions in Function.prototype.{call,apply}
2020-05-30 10:33:24 +02:00
Jack Karamanian
f4129ac422 LibJS: Use the function's bound |this| and bound arguments in
Interpreter::call()
2020-05-30 10:33:24 +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
Matthew Olsson
4e331a1fcf LibJS: Object.getOwnPropertyDescriptor works properly with accessors 2020-05-30 10:32:52 +02:00
Marcin Gasperowicz
4e8de753c9 LibJS: Parse arrow function expression with correct precedence
The parser was chomping on commas present after the arrow function expression. eg. [x=>x,2] would parse as [x=>(x,2)] instead of [(x=>x),2].

This is not the case anymore. I've added a small test to prove this.
2020-05-30 00:33:18 +02:00
Matthew Olsson
d52ea37717 LibJS: Integrate labels into the Interpreter
The interpreter now considers a statement or block's label when
considering whether or not to break. All statements can be labelled.
2020-05-29 16:20:32 +02:00
Matthew Olsson
10bf4ba3dc LibJS: Parse labelled statements
All statements now have an optional label string that can be null.
2020-05-29 16:20:32 +02:00
Matthew Olsson
5cd01ed79e LibJS: New expressions look for expressions with correct precedence 2020-05-29 15:56:39 +02:00
Linus Groh
1dd44210b7 LibJS: Add Array.prototype.toLocaleString() 2020-05-29 08:00:02 +02:00
Linus Groh
70d2add22f LibJS: Add Object.prototype.toLocaleString() 2020-05-29 08:00:02 +02:00
Matthew Olsson
664085b719 LibJS: Fix conditional expression precedence
This fixes the following from parsing incorrectly due to the comma
that occurs after the conditional:

  let o = {
    foo: true ? 1 : 2,
    bar: 'baz',
  };
2020-05-29 07:57:14 +02:00
Linus Groh
8ff4587f65 LibJS: Throw in strict mode when assigning property to primitive value 2020-05-29 07:45:22 +02:00
Marcin Gasperowicz
eadce65e04
LibJS: Implement standard semantics for relational operators (#2417)
Previously, the relational operators where casting any value to double
and comparing the results according to C++ semantics.

This patch makes the relational operators in JS behave according to the
standard specification.

Since we don't have BigInt yet, the implementation doesn't take it into
account. 

Moved PreferredType from Object to Value. Value::to_primitive now
passes preferred_type to Object::to_primitive.
2020-05-28 17:19:59 +02:00
Matthew Olsson
786722149b LibJS: Add strict mode
Adds the ability for a scope (either a function or the entire program)
to be in strict mode. Scopes default to non-strict mode.

There are two ways to determine the strict-ness of the JS engine:

1. In the parser, this can be accessed with the parser_state variable
   m_is_strict_mode boolean. If true, the Parser is currently parsing in
   strict mode. This is done so that the Parser can generate syntax
   errors at parse time, which is required in some cases.

2. With Interpreter.is_strict_mode(). This allows strict mode checking
   at runtime as opposed to compile time.

Additionally, in order to test this, a global isStrictMode() function
has been added to the JS ReplObject under the test-mode flag.
2020-05-28 17:18:42 +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
Angel
199a6b40b3 LibJS: Add Array.prototype.fill 2020-05-26 20:34:44 +02:00
Luke
f9f7cb4583 LibJS: Add Array.prototype.splice
Adds splice to Array.prototype according to the specification:
https://tc39.es/ecma262/#sec-array.prototype.splice
2020-05-26 12:47:11 +02:00
Linus Groh
07af2e6b2c LibJS: Implement basic for..in and for..of loops 2020-05-25 18:45:36 +02:00
Linus Groh
92fd140cb2 LibJS: Make Array.prototype.includes() generic 2020-05-24 23:51:14 +02:00
Linus Groh
e78bc2f6fd LibJS: Make Array.prototype.lastIndexOf() generic 2020-05-24 23:51:14 +02:00
Linus Groh
9b9b6a4cff LibJS: Make Array.prototype.indexOf() generic 2020-05-24 23:51:14 +02:00
Marcin Gasperowicz
99991761fd LibJS: add Array.prototype.reduceRight()
This patch adds `Array.prototype.reduceRight()` method to LibJS Runtime. The implementation is (to my best knowledge) conformant to https://tc39.es/ecma262/#sec-array.prototype.reduceright.

Short test in `LibJS/Tests/Array.prototype-generic-functions.js` demonstrates that the function can be applied to other objects besides `Array`.
2020-05-24 20:58:14 +02:00
Marcin Gasperowicz
27c71d2627
LibJS: Add Array.prototype.reduce() (#2334)
This patch adds `Array.prototype.reduce()` method to LibJS Runtime.
The implementation is (to my best knowledge) comformant to ECMA262.

The test `Array.prototype-generic-functions.js` demonstrates that the
function can be applied to other objects besides `Array`.
2020-05-23 16:41:25 +02:00
Linus Groh
00fe7f82c0 LibJS: Treat NaN in Value::to_i32() as zero
Let's treat it as zero like the ECMAScript spec does in toInteger().

That way we can use to_i32() and don't have to care about weird input
input values where a number is expected, i.e.

"foo".charAt() === "f"
"foo".charAt("bar") === "f"
"foo".charAt(0) === "f"
2020-05-23 16:39:17 +02:00
Linus Groh
843e000f18 LibJS: Fix Array.prototype.lastIndexOf() implementation 2020-05-23 00:02:13 +02:00
Linus Groh
6a4280e6e5 LibJS: Treat missing arg in Array.prototype.{indexOf,lastIndexOf}() as undefined 2020-05-23 00:02:13 +02:00
Linus Groh
040c75a3cc LibJS: Make Array.prototype.{join,toString}() generic 2020-05-22 17:43:44 +02:00
Linus Groh
e9ee06b19e LibJS: Make Array.prototype.pop() generic 2020-05-22 17:43:44 +02:00
Linus Groh
4334a1b208 LibJS: Make Array.prototype.push() generic 2020-05-22 17:43:44 +02:00
Linus Groh
9f7a6e116a LibJS: Let Array.prototype.join() ignore additional arguments
I.e.

array.join("x", "y", "z") === array.join("x")

rather than

array.join("x", "y", "z") === array.join()
2020-05-22 17:43:44 +02:00
Matthew Olsson
c35732c011 LibJS: Add object literal getter/setter shorthand
Adds support for the following syntax:

let foo = {
    get x() {
        // ...
    },
    set x(value) {
        // ...
    }
}
2020-05-22 10:59:05 +02:00
Linus Groh
27913154ea LibJS: Disallow multiple parameters in paren-less arrow function
Fixes #2323.
2020-05-22 00:50:57 +02:00
Matthew Olsson
45dfa094e9 LibJS: Add getter/setter support
This patch adds a GetterSetterPair object. Values can now store pointers
to objects of this type. These objects are created when using
Object.defineProperty and providing an accessor descriptor.
2020-05-21 22:56:18 +02:00
Linus Groh
a4d04cc748 LibJS: Refactor Array.prototype callback functions and make them generic 2020-05-21 22:50:14 +02:00
Linus Groh
5db9becc4a LibJS: Treat missing arg in Array.prototype.includes() as undefined 2020-05-21 22:50:14 +02:00
Luke
57d15acd4c LibJS: Add Array.prototype.every 2020-05-21 19:44:59 +02:00
Matthew Olsson
e415dd4e9c LibJS: Handle hex and unicode escape sequences in string literals
Introduces the following syntax:

'\x55'
'\u26a0'
'\u{1f41e}'
2020-05-18 17:58:17 +02:00
Linus Groh
b3090678a9 LibJS: Add Math.clz32() 2020-05-18 17:57:28 +02:00
Linus Groh
452dbbc463 LibJS: Add Math.expm1() 2020-05-18 17:57:28 +02:00
Linus Groh
e375766f98 LibJS: Add Math.exp() 2020-05-18 17:57:28 +02:00
Linus Groh
b27834cf16 LibJS: Add Math.sign() 2020-05-18 17:57:28 +02:00
Linus Groh
4569e88bea LibJS: Throw TypeError when coercing symbol to number 2020-05-18 09:39:55 +02:00
Linus Groh
476094922b LibJS: Pass Interpreter& to Value::to_number() et al.
This patch is unfortunately rather large and might make some things feel
bloated, but it is necessary to fix a few flaws in LibJS, primarily
blindly coercing values to numbers without exception checks - i.e.

interpreter.argument(0).to_i32();  // can fail!!!

Some examples where the interpreter would actually crash:

var o = { toString: () => { throw Error() } };
+o;
o - 1;
"foo".charAt(o);
"bar".repeat(o);

To fix this, we now have the following...

to_double(Interpreter&)
to_i32()
to_i32(Interpreter&)
to_size_t()
to_size_t(Interpreter&)

...and a whole lot of exception checking.

There's intentionally no to_double(), use as_double() directly instead.

This way we still can use these convenient utility functions but don't
need to check for exceptions if we are sure the value already is a
number.

Fixes #2267.
2020-05-18 09:39:55 +02:00
mattco98
4ced126704 LibJS: Add symbol objects
This commit adds the following classes: SymbolObject, SymbolConstructor,
SymbolPrototype, and Symbol. This commit does not introduce any
new functionality to the Object class, so they cannot be used as
property keys in objects.
2020-05-17 18:05:15 +02:00
Linus Groh
73eef31f49 LibJS: Add Number.parseFloat() 2020-05-17 16:21:38 +02:00
Linus Groh
6f6b089aa0 LibJS: Add parseFloat() 2020-05-17 16:21:33 +02:00
Linus Groh
b299c75d45 LibJS: Make Object.prototype.constructor non-enumerable 2020-05-16 21:22:34 +02:00
Andreas Kling
de7827faf7 LibM: Fix floor() and floorf() for negative numbers
And add a LibJS test to exercise the code. :^)
2020-05-15 22:05:59 +02:00
Andreas Kling
c6ddbd1f3e LibJS: Add side-effect-free version of Value::to_string()
There are now two API's on Value:

- Value::to_string(Interpreter&) -- may throw.
- Value::to_string_without_side_effects() -- will never throw.

These are some pretty big sweeping changes, so it's possible that I did
some part the wrong way. We'll work it out as we go. :^)

Fixes #2123.
2020-05-15 13:50:42 +02:00
Linus Groh
33defef267 LibJS: Let parser keep track of errors
Rather than printing them to stderr directly the parser now keeps a
Vector<Error>, which allows the "owner" of the parser to consume them
individually after parsing.

The Error struct has a message, line number, column number and a
to_string() helper function to format this information into a meaningful
error message.

The Function() constructor will now include an error message when
throwing a SyntaxError.
2020-05-15 09:53:52 +02:00
Sergey Bugaev
450a2a0f9c Build: Switch to CMake :^)
Closes https://github.com/SerenityOS/serenity/issues/2080
2020-05-14 20:15:18 +02:00
Linus Groh
de42ddfd93 LibJS: Trim whitespace from string before coercing to number 2020-05-13 09:36:20 +02:00
Linus Groh
0c14ee035c LibJS: Make string to number coercion work for doubles 2020-05-13 09:36:20 +02:00
Linus Groh
7b8765c311 LibJS: Make the Function() constructor throw a SyntaxError, not return 2020-05-13 09:34:25 +02:00
Linus Groh
d69ed91790 LibJS: Check AssignmentExpression LHS in parser
There are many cases which shouldn't even parse, like

null = ...
true = ...
false = ...
123 = ...
"foo" = ...

However this *is* valid syntax:

foo() = ...

So we still have to keep the current code doing a runtime check if the
LHS value is a resolvable reference. I believe this was declared valid
syntax to *in theory* allow functions returning references - though in
practice that isn't a thing.

Fixes #2204.
2020-05-13 01:15:29 +02:00
Linus Groh
8ffdcce0d0 LibJS: Parse comma operator into SequenceExpression 2020-05-11 18:36:28 +02:00
Linus Groh
e333b60064 LibJS: Add Array.of() 2020-05-08 20:06:49 +02:00
Linus Groh
ca22476d9d LibJS: Add Array.isArray() 2020-05-08 20:06:49 +02:00
Linus Groh
01fd6ce045 LibJS: Support multiple arguments in Array constructor 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
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
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
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
Linus Groh
454c1e6bbe LibJS: Implement rest parameters 2020-05-04 23:30:52 +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
Linus Groh
99be27b4a1 LibJS: Add "name" property to functions 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
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
Kesse Jones
6dbb5df81f LibJS: Add String.prototype.lastIndexOf 2020-05-01 16:50:37 +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
Linus Groh
8159f45f6e LibJS: Make String.prototype.slice() generic 2020-04-29 19:14:36 +02:00
Linus Groh
cfdb7b8806 LibJS: Make (most) String.prototype functions generic
I.e. they don't require the |this| value to be a string object and
"can be transferred to other kinds of objects for use as a method" as
the spec describes it.
2020-04-29 18:53:21 +02:00
mattco98
95abcc3722 LibJS: Implement correct object property ordering
This commit introduces a way to get an object's own properties in the
correct order. The "correct order" for JS object properties is first all
array-like index properties (numeric keys) sorted by insertion order,
followed by all string properties sorted by insertion order.

Objects also now print correctly in the repl! Before this commit:

courage ~/js-tests $ js
> ({ foo: 1, bar: 2, baz: 3 })
{ bar: 2, foo: 1, baz: 3 }

After:

courage ~/js-tests $ js
> ({ foo: 1, bar: 2, baz: 3 })
{ foo: 1, bar: 2, baz: 3 }
2020-04-29 18:47:03 +02:00
Kesse Jones
58f6f50de4 LibJS: Add String.prototype.slice 2020-04-29 18:35:18 +02:00
Linus Groh
d4ec38097f LibJS: Return undefined in Array.prototype.{pop,shift} for empty values 2020-04-29 09:38:25 +02:00
Linus Groh
da0ab16f01 LibJS: Don't handle arrays separately in Value::to_number()
Now that Array.prototype.join() is producing the correct results we
can remove the separate code path for arrays in Value::to_number()
and treat them like all other objects - using to_primitive() with
number as the preferred type and then calling to_number() on the
result.

This is how the spec descibes it.

This also means we don't crash anymore when trying to coerce
[<empty>] to a number - it now does the following:

[<empty>] - to string - "" - to number - 0
[<empty>, <empty>] - to string - "," - to number - NaN
2020-04-29 01:30:59 +02:00
Linus Groh
6d6cd64689 LibJS: Skip undefined and null in join_array_with_separator()
This it being used in Array.prototype.{join,toString}() - and now
adhering to the spec: [undefined, null].join() === ","
2020-04-29 01:30:59 +02:00
Linus Groh
8ad11df89f LibJS: Handle exception in for loop test execution 2020-04-28 23:11:18 +02:00
mattco98
104969a9f5 LibJS: Add spreading in object literals
Supports spreading strings, arrays, and other objects within object
literals.
2020-04-28 20:37:21 +02:00
Linus Groh
3152559422 LibJS: Call Array.prototype.findIndex() callback for empty elements
If the array value at the current index is empty, the callback will
be called with undefined as value.
2020-04-28 20:15:38 +02:00
Linus Groh
823cc7bc1c LibJS: Call Array.prototype.find() callback for empty elements
If the array value at the current index is empty, the callback will
be called with undefined as value.
2020-04-28 20:15:38 +02:00
Linus Groh
ad8abce8a5 LibJS: Let Array.prototype.map() resize new array before loop
Currently we would create an empty array of size 0 and appening results
of the callback function while skipping empty values.

This is incorrect, we should be initializing a full array of the correct
size beforehand and then inserting the results while still skipping
empty values.

Wrong: new Array(5).map(() => {}) // []
Right: new Array(5).map(() => {}) // [<empty> * 5]
2020-04-28 20:15:38 +02:00
Linus Groh
0a0ba64383 LibJS: Handle Object.prototype.hasOwnProperty() with no arg correctly
I.e. the same as hasOwnProperty(undefined) or
hasOwnProperty("undefined") :^)
2020-04-28 20:03:50 +02:00