This patch adds a new kind of JS::Value, the empty value.
It's what you get when you do JSValue() (or most commonly, {} in C++.)
An empty Value signifies the absence of a value, and should never be
visible to JavaScript itself. As of right now, it's used for array
holes and as a return value when an exception has been thrown and we
just want to unwind.
This patch is a bit of a mess as I had to fix a whole bunch of code
that was relying on JSValue() being undefined, etc.
Now that we have two separate storages for Object properties depending
on what kind of index they have, it's nice to have an abstraction that
still allows us to say "here's a property name".
We use PropertyName to always choose the optimal storage path directly
while interpreting the AST. :^)
Objects can have both named and indexed properties. Previously we kept
all property names as strings. This patch separates named and indexed
properties and splits them between Object::m_storage and m_elements.
This allows us to do much faster array-style access using numeric
indices. It also makes the Array class much less special, since all
Objects now have number-indexed storage. :^)
We were allowing this dangerous kind of thing:
RefPtr<Base> base;
RefPtr<Derived> derived = base;
This patch changes the {Nonnull,}RefPtr constructors so this is no
longer possible.
To downcast one of these pointers, there is now static_ptr_cast<T>:
RefPtr<Derived> derived = static_ptr_cast<Derived>(base);
Fixing this exposed a ton of cowboy-downcasts in various places,
which we're now forced to fix. :^)
Introduce support for the both of these Math methods.
Math.trunc is implemented in terms of Math.ceil or Math.floor
based on the input value. Added tests as well.
This change implements floating point mod based on the algorithm
used in LibM's fmod() implementation. To avoid taking a dependency
on LibM from LibJS I reimplemented the formula in LibJS.
I've incuded some of the example MDM test cases as well.
This surfaced and issue handling NaN which I've fixed as well.
Address the FIXME in MathObject::max to handle an arbitrary
number of arguments. Also adding a test case to verify the
behavior of Math.max() while I'm here.
There is no such thing as a "undefined literal" in JS - undefined is
just a property on the global object with a value of undefined.
This is pretty similar to NaN.
var undefined = "foo"; is a perfectly fine AssignmentExpression :^)
This patch adds JS::Shape, which implements a transition tree for our
Object class. Object property keys, prototypes and attributes are now
stored in a Shape, and each Object has a Shape.
When adding a property to an Object, we make a transition from the old
Shape to a new Shape. If we've made the same exact transition in the
past (with another Object), we reuse the same transition and both
objects may now share a Shape.
This will become the foundation of inline caching and other engine
optimizations in the future. :^)
Let's move towards using references over pointers in LibJS as well.
I had originally steered away from it because that's how I've seen
things done in other engines. But this is not the other engines. :^)
This adds Function::construct() for constructor function calls via `new`
keyword. NativeFunction doesn't have constructor behaviour by default,
ScriptFunction simply calls call() in construct()
LibWeb now creates a WindowObject which inherits from GlobalObject.
Allocation of the global object is moved out of the Interpreter ctor
to allow for specialized construction.
The existing Window interfaces are moved to WindowObject with their
implementation code in the new Window class.
This adds:
- A global Date object (with `length` property and `now` function)
- The Date constructor (no arguments yet)
- The Date prototype (with `get*` functions)
- An empty string is converted to 0
- An empty array is converted to 0
- An array with one item is converted to that item's numeric value
- An array with more than one item is converted to NaN
Instead of implementing every native function as a lambda function,
use static member functions instead.
This makes it easier to navigate the code + backtraces look nicer. :^)
Native functions now only get the Interpreter& as an argument. They can
then extract |this| along with any indexed arguments it wants from it.
This forces functions that want |this| to actually deal with calling
interpreter.this_value().to_object(), and dealing with the possibility
of a non-object |this|.
This is still not great but let's keep massaging it forward.
Fix the "instanceof" operator to check if the constructor's prototype
property occurs anywhere in the prototype chain of the instance object.
This patch also adds Object.setPrototypeOf() to make it possible to
create a test for this bug.
Thanks to DexesTTP for pointing this out! :^)
This operator walks the prototype chain of the RHS value and looks for
a "prototype" property with the same value as the prototype of the LHS.
This is pretty cool. :^)
We were interpreting "undefined" as a variable lookup failure in some
cases and throwing a ReferenceError exception instead of treating it
as the valid value "undefined".
This patch wraps the result of variable lookup in Optional<>, which
allows us to only throw ReferenceError when lookup actually fails.
You can now throw an expression to the nearest catcher! :^)
To support throwing arbitrary values, I added an Exception class that
sits as a wrapper around whatever is thrown. In the future it will be
a logical place to store a call stack.
You can now throw exceptions by calling Interpreter::throw_exception().
Anyone who calls ASTNode::execute() needs to check afterwards if the
Interpreter now has an exception(), and if so, stop what they're doing
and simply return.
When catching an exception, we'll first execute the CatchClause node
if present. After that, we'll execute the finalizer block if present.
This is unlikely to be completely correct, but it's a start! :^)
Object now has virtual get_own_property() and put_own_property() member
functions that can be overridden to provide custom behavior.
We use these virtuals to move Array-specific access behavior to Array.
This function is ultimately supposed to be generic and allow any |this|
that has a length property, but for now it only works on our own Array
object type.
I'm not completely thrilled about Object::get() and Object::put() doing
special-case stuff for arrays, and we should probably come up with a
better abstraction for it.
But at least it works for now, which is really nice. :^)
This is pretty naive, we just walk up the prototype chain and call any
NativeProperty setter that we find. If we don't find one, we put/set
the value as an own property of the object itself.
Now that Interpreter keeps all arguments in the CallFrame stack, we can
just pass a const-reference to the CallFrame's argument vector to each
function handler (instead of copying it.)