Stage 3 since August 2019 - we already have shebang stripping
implemented in js(1), so this removes it from there in favor of adding
support to the lexer directly.
Most straightforward proposal and implementation I've ever seen :^)
https://github.com/tc39/proposal-hashbang
js only accepted a single script file to run before this. With this
patch, multiple scripts can be run in the same execution environment,
allowing the user to specify a "preamble script" to be executed before
the main script.
This commit adds a bunch of passes, the most interesting of which is a
pass that merges blocks together, and a pass that places blocks that
flow into each other next to each other, and a very simply pass that
removes duplicate basic blocks.
Note that this does not remove the jump at the end of each block in that
pass to avoid scope creep in the passes.
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.
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 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 patch begins the work of implementing JavaScript execution in a
bytecode VM instead of an AST tree-walk interpreter.
It's probably quite naive, but we have to start somewhere.
The basic idea is that you call Bytecode::Generator::generate() on an
AST node and it hands you back a Bytecode::Block filled with
instructions that can then be interpreted by a Bytecode::Interpreter.
This first version only implements two instructions: Load and Add. :^)
Each bytecode block has infinity registers, and the interpreter resizes
its register file to fit the block being executed.
Two new `js` options are added in this patch as well:
`-d` will dump the generated bytecode
`-b` will execute the generated bytecode
Note that unless `-d` and/or `-b` are specified, none of the bytecode
related stuff in LibJS runs at all. This is implemented in parallel
with the existing AST interpreter. :^)
This replaces the two sloppy copies of the load() function with a
cleaned up implementation:
- Only use the first argument, to load multiple files just call the
function multiple times
- Fix a crash when using any non-string argument
- Throw an error if the file can't be opened instead of logging to
stderr
- Don't use parse_and_run(), which would print the AST of the loaded
file when using -A, for example - it's used either way as the entry
point in both REPL and non-REPL mode, so we already get exception
handling and all that
If we don't have any bytes to print in hex representation, just return
early instead of printing a newline in preparation for the data that
won't follow. :^)
Instead of storing the function names (in a badly named Vector<String>)
and source ranges separately, consolidate them into a new struct:
TracebackFrame. This makes it both easier to use now and easier to
extend in the future.
Unlike before we now keep each call frame's current node source range
in the traceback frame next to the function name, meaning we can display
line and column numbers outside of the VM and after the call stack is
emptied.
SPDX License Identifiers are a more compact / standardized
way of representing file license information.
See: https://spdx.dev/resources/use/#identifiers
This was done with the `ambr` search and replace tool.
ambr --no-parent-ignore --key-from-file --rep-from-file key.txt rep.txt *
The previous handling of the name and message properties specifically
was breaking websites that created their own error types and relied on
the error prototype working correctly - not assuming an JS::Error this
object, that is.
The way it works now, and it is supposed to work, is:
- Error.prototype.name and Error.prototype.message just have initial
string values and are no longer getters/setters
- When constructing an error with a message, we create a regular
property on the newly created object, so a lookup of the message
property will either get it from the object directly or go though the
prototype chain
- Internal m_name/m_message properties are no longer needed and removed
This makes printing errors slightly more complicated, as we can no
longer rely on the (safe) internal properties, and cannot trust a
property lookup either - get_without_side_effects() is used to solve
this, it's not perfect but something we can revisit later.
I did some refactoring along the way, there was some really old stuff in
there - accessing vm.call_frame().arguments[0] is not something we (have
to) do anymore :^)
Fixes#6245.
We now leverage the VM's promise rejection tracker callbacks and print a
warning in either of these cases:
- A promise was rejected without any handlers
- A handler was added to an already rejected promise
If in 'foo(); bar();' bar fails, we'd get the error of that and then
foo's return value - that's probably not something anyone expects.
Also make sure to return non-success so the process will exit with 1.
This is basically just for consistency, it's quite strange to see
multiple AK container types next to each other, some with and some
without the namespace prefix - we're 'using AK::Foo;' a lot and should
leverage that. :^)
(...and ASSERT_NOT_REACHED => VERIFY_NOT_REACHED)
Since all of these checks are done in release builds as well,
let's rename them to VERIFY to prevent confusion, as everyone is
used to assertions being compiled out in release.
We can introduce a new ASSERT macro that is specifically for debug
checks, but I'm doing this wholesale conversion first since we've
accumulated thousands of these already, and it's not immediately
obvious which ones are suitable for ASSERT.
If an exception was thrown while printing the last computed value in
the REPL, it would always assert on next input.
Something like this would always assert:
> a=[];Object.defineProperty(a,"0",{get:()=>{throw ""}})
> 1 + 2
This API was a mostly gratuitous deviation from POSIX that gave up some
portability in exchange for avoiding the occasional strlen().
I don't think that was actually achieving anything valuable, so let's
just chill out and have the same open() API as everyone else. :^)