The PropertyName class able to match a number or an array can only
accept positive numerical values. However, the computed_property_name
method sometimes returned negative values.
This commit also adds a basic object access test case.
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. :^)
This patch adds support in the parser and interpreter for this:
var a = 1, b = 2, c = a + b;
VariableDeclaration is now a sequence of VariableDeclarators. :^)
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 :^)
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()
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.
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. :^)
NewExpression mostly piggybacks on the existing CallExpression. The big
difference is that "new" creates a new Object and passes it as |this|
to the callee.
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! :^)
- move() the property map when constructing ObjectExpression instead of
making a copy.
- Use key+value iterators to traverse the property map in the execute()
and dump() functions.
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.