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.
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
"[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
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.
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.
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'
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.
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 }
This patch teaches UpdateExpression how to use a Reference. Some other
changes were necessary to keep tests working:
A Reference can now also refer to a local or global variable. This is
not fully aligned with the spec since we don't have a Record concept.
Expression nodes can now be asked to produce a Reference. We then use
this to implement the "delete" operator without downcasting the child
node to a MemberExpression manually.
Implement the syntax and behavor necessary to support array literals
such as [...[1, 2, 3]]. A type error is thrown if the target of the
spread operator does not evaluate to an array (though it should
eventually just check for an iterable).
Note that the spread token's name is TripleDot, since the '...' token is
used for two features: spread and rest. Calling it anything involving
'spread' or 'rest' would be a bit confusing.
It turns out "delete" is actually a unary op :)
This patch implements deletion of object properties, it doesn't yet
work for casually deleting properties from the global object.
When deleting a property from an object, we switch that object to
having a unique shape, no longer sharing shapes with others.
Once an object has a unique shape, it no longer needs to care about
shape transitions.
JS::Value already has the empty state ({} or Value() gives you one.)
Use this instead of wrapping Value in Optional in some places.
I've also added Value::value_or(Value) so you can easily provide a
fallback value when one is not present.
- Let undefined variables throw a ReferenceError by using
Identifier::execute() rather than doing variable lookup manually and
ASSERT()ing
- Coerce value to number rather than ASSERT()ing
- Make code DRY
- Add tests
A MarkedValueList is basically a Vector<JS::Value> that registers with
the Heap and makes sure that the stored values don't get GC'd.
Before this change, we were unsafely keeping Vector<JS::Value> in some
places, which is out-of-reach for the live reference finding logic
since Vector puts its elements on the heap by default.
We now pass all the JavaScript tests even when running with "js -g",
which does a GC on every heap allocation.
Everyone who constructs an Object must now pass a prototype object when
applicable. There's still a fair amount of code that passes something
fetched from the Interpreter, but this brings us closer to being able
to detach prototypes from Interpreter eventually.
Let's start moving towards native JS objects taking their prototype as
a constructor argument.
This will eventually allow us to move prototypes off of Interpreter and
into GlobalObject.
This patch replaces the old variable lookup logic with a new one based
on lexical environments.
This brings us closer to the way JavaScript is actually specced, and
also gives us some basic support for closures.
The interpreter's call stack frames now have a pointer to the lexical
environment for that frame. Each lexical environment can have a chain
of parent environments.
Before calling a Function, we first ask it to create_environment().
This gives us a new LexicalEnvironment for that function, which has the
function's lexical parent's environment as its parent. This allows
inner functions to access variables in their outer function:
function foo() { <-- LexicalEnvironment A
var x = 1;
function() { <-- LexicalEnvironment B (parent: A)
console.log(x);
}
}
If we return the result of a function expression from a function, that
new function object will keep a reference to its parent environment,
which is how we get closures. :^)
I'm pretty sure I didn't get everything right here, but it's a pretty
good start. This is quite a bit slower than before, but also correcter!