More specifically, Array.prototype.splice. Additionally adds a missing
exception check to the array creation and a link to the spec.
Fixes create-non-array-invalid-len.js in the splice tests in test262.
This test timed out instead of throwing an "Invalid array length"
exception.
We already have two separate implementations of this, so let's do it
properly. The optional value type check is done by a callback function
that returns Result<void, ErrorType> - value type accepted or message
for TypeError, that is.
"let" and "const" go in the lexical environment.
This fixes one part of #4001 (Lexically declared variables are mixed up
with global object properties)
This was creating a ton of pointless busywork for the garbage collector
and can be avoided simply by tolerating that the current call frame has
a null scope object for the duration of a NativeFunction activation.
This makes sure that is<Set> checks done on the Set prototype instead of
on Set instances return false, thereby emulating the behaviour of the
RequireInternalSlot abstract operation.
Instead of using Strings in the bytecode ops this adds a global string
table to the Executable struct which individual operations can refer
to using indices. This brings bytecode ops one step closer to being
pointer free.
While this implementation should be complete it is based on HashTable's
iterator, which currently follows bucket-order instead of the required
insertion order. This can be simply fixed by replacing the underlying
HashTable member in Set with an enhanced one that maintains a linked
list in insertion order.
Added Increment and Decrement bytecode ops to support this. Postfix
updates use a temporary register to preserve the original value.
Note that this patch only implements Identifier updates. Member
expression updates are a TODO.
This limits the size of each block (currently set to 1K), and gets us
closer to a canonical, more easily analysable bytecode format.
As a result of this, "Labels" are now simply entries to basic blocks.
Since there is no more 'conditional' jump (as all jumps are always
taken), JumpIf{True,False} are unified to JumpConditional, and
JumpIfNullish is renamed to JumpNullish.
Also fixes#7914 as a result of reimplementing the loop logic.
This change removes the mmap inside of Block in favor of a growing
vector of bytes. This is favorable for two reasons:
- We don't take more space than we need
- There is no limit to the growth of the vector (previously, if
the Block overstepped its 64kb boundary, it would just crash)
However, if that vector happens to resize, any pointer pointing into
that vector would become invalid. To avoid this, this commit adds an
InstructionHandle<Op> class which just stores a block and an offset
into that block.
This was missing from Value::is_array(), which is equivalent to the
spec's IsArray() abstract operation - it treats a Proxy value with an
Array target object as being an Array.
It can throw, so needs both the global object and an exception check
now.
This ensures that "while", do...while, "for" expressions have a
properly initialized result value even if the user terminated
the loop body via break or the loop body wasn't executed at all.
Previously SwitchStatement::execute() would return <empty> when hitting
break, continue or empty consequent block. This was not in line with
the standard.
This partially reverts c6ce7c9326.
The munmap part of that change was good, but we can't seal the blocks
since that breaks NewString and other ops that have String members.
This commit introduces the concept of an accumulator register to
LibJS's bytecode interpreter. The accumulator register is always
register 0, and most simple instructions use it for reading and
writing.
Not only does this slim down the AST, but it also simplifies a lot of
the code. For example, the generate_bytecode methods no longer need
to return an Optional<Register>, as any opcode which has a "return"
value will always put it into the accumulator.
This also renames the old Op::Load to Op::LoadImmediate, and uses
Op::Load to load from a register into the accumulator. There is
also an Op::Store to put the value in the accumulator into another
register.