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 :)
- 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
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).
This is a helper function based on the getter/setter definition logic from
ObjectExpression::execute() to look up an Accessor property if it already
exists, define a new Accessor property if it doesn't exist, and set the getter or
setter function on the Accessor.
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.
Divide the Object constructor into three variants:
- The regular one (takes an Object& prototype)
- One for use by GlobalObject
- One for use by objects without a prototype (e.g ObjectPrototype)
Instead of taking the JS::Heap&. This allows us to get rid of some
calls to JS::Interpreter::global_object(). We're getting closer and
closer to multiple global objects. :^)
We're still missing optional argument support, so this implementation
doesn't support fill(), only fill(fill_rule).
Still it's really nice to get rid of so much hand-written wrapper code.
To allow implementing the DOM class hierarchy in JS bindings, this
patch adds an inherits() function that can be used to ask an Object
if it inherits from a specific C++ class (by name).
The necessary overrides are baked into each Object subclass by the
new JS_OBJECT macro, which works similarly to C_OBJECT in LibCore.
Thanks to @Dexesttp for suggesting this approach. :^)
Instead of only checking the class_name(), we now generate an is_foo()
virtual in the wrapper generator. (It's currently something we override
on Bindings::Wrapper, which is not really scalable.)
Longer term we'll need to think up something smarter for verifying that
one wrapper "is" another type of wrapper.
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.
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.
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.
Get rid of the weird old signature:
- int StringType::to_int(bool& ok) const
And replace it with sensible new signature:
- Optional<int> StringType::to_int() const
The interpreter now has an "underscore is last value" flag, which makes
Interpreter::get_variable() return the last value if:
- The m_underscore_is_last_value flag is enabled
- The name of the variable lookup is "_"
- The result of that lookup is an empty value
That means "_" can still be used as a regular variable and will stop
doing its magic once anything is assigned to it.
Example REPL session:
> 1
1
> _ + _
2
> _ + _
4
> _ = "foo"
"foo"
> 1
1
> _
"foo"
> delete _
true
> 1
1
> _
1
>
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.
We need to move towards supporting multiple global objects, which will
be a large refactoring. To keep it manageable, let's do it in steps,
starting with giving Object a way to find the GlobalObject it lives
inside by asking its Shape for it.