2020-03-07 18:42:11 +00:00
|
|
|
/*
|
2021-06-21 22:55:03 +00:00
|
|
|
* Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
|
2022-08-26 23:54:55 +00:00
|
|
|
* Copyright (c) 2020-2022, Linus Groh <linusg@serenityos.org>
|
2020-03-07 18:42:11 +00:00
|
|
|
*
|
2021-04-22 08:24:48 +00:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-03-07 18:42:11 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
LibJS: Let MarkedVector<T> inherit from Vector and handle Cell* + Value
Note: MarkedVector is still relatively new and has zero users right now,
so these changes don't affect any code other than the class itself.
Reasons for this are the rather limited API:
- Despite the name and unlike MarkedValueList, MarkedVector isn't
actually a Vector, it *wraps* a Vector. This means that plenty of
convenient APIs are unavailable and have to be exported on the class
separately and forwarded to the internal Vector, or need to go through
the exposed Span - both not great options.
- Exposing append(Cell*) and prepend(Cell*) on the base class means that
it was possible to append any Cell type, not just T! All the strong
typing guarantees are basically gone, and MarkedVector doesn't do much
more than casting Cells to the appropriate type through the exposed
Span.
All of this combined means that MarkedVector - in its current form -
doesn't provide much value over MarkedValueList, and that we have to
maintain two separate, yet almost identical classes.
Let's fix this!
The updated MarkedVector steals various concepts from the existing
MarkedValueList, especially the ability to copy. On the other hand, it
remains generic enough to handle both Cell* and Value for T, making
MarkedValueList effectively redundant :^)
Additionally, by inheriting from Vector we get all the current and
future APIs without having to select and expose them separately.
MarkedVectorBase remains and takes care of communicating creation and
destruction of the class to the heap. Visiting the contained values is
handled via a pure virtual method gather_roots(), which is being called
by the Heap's function of the same name; much like the VM has one.
From there, values are added to the roots HashTable if they are cells
for T = Value, and unconditionally for any other T.
As a small additional improvement the template now also takes an
inline_capacity parameter, defaulting to 32, and forwards it to the
Vector template; allowing for possible future optimizations of current
uses of MarkedValueList, which hard-codes it to 32.
2022-02-09 09:40:49 +00:00
|
|
|
#include <AK/Types.h>
|
|
|
|
|
2021-10-19 17:18:01 +00:00
|
|
|
#define JS_DECLARE_NATIVE_FUNCTION(name) \
|
2022-08-22 10:48:08 +00:00
|
|
|
static JS::ThrowCompletionOr<JS::Value> name(JS::VM&)
|
2021-10-19 17:18:01 +00:00
|
|
|
|
|
|
|
#define JS_DEFINE_NATIVE_FUNCTION(name) \
|
2022-08-22 10:48:08 +00:00
|
|
|
JS::ThrowCompletionOr<JS::Value> name([[maybe_unused]] JS::VM& vm)
|
2021-10-19 17:18:01 +00:00
|
|
|
|
2020-11-30 22:50:43 +00:00
|
|
|
// NOTE: Proxy is not included here as it doesn't have a prototype - m_proxy_constructor is initialized separately.
|
2021-11-15 00:53:24 +00:00
|
|
|
#define JS_ENUMERATE_NATIVE_OBJECTS_EXCLUDING_TEMPLATES \
|
|
|
|
__JS_ENUMERATE(AggregateError, aggregate_error, AggregateErrorPrototype, AggregateErrorConstructor, void) \
|
|
|
|
__JS_ENUMERATE(Array, array, ArrayPrototype, ArrayConstructor, void) \
|
|
|
|
__JS_ENUMERATE(ArrayBuffer, array_buffer, ArrayBufferPrototype, ArrayBufferConstructor, void) \
|
|
|
|
__JS_ENUMERATE(AsyncFunction, async_function, AsyncFunctionPrototype, AsyncFunctionConstructor, void) \
|
|
|
|
__JS_ENUMERATE(AsyncGeneratorFunction, async_generator_function, AsyncGeneratorFunctionPrototype, AsyncGeneratorFunctionConstructor, void) \
|
|
|
|
__JS_ENUMERATE(BigIntObject, bigint, BigIntPrototype, BigIntConstructor, void) \
|
|
|
|
__JS_ENUMERATE(BooleanObject, boolean, BooleanPrototype, BooleanConstructor, void) \
|
|
|
|
__JS_ENUMERATE(DataView, data_view, DataViewPrototype, DataViewConstructor, void) \
|
|
|
|
__JS_ENUMERATE(Date, date, DatePrototype, DateConstructor, void) \
|
|
|
|
__JS_ENUMERATE(Error, error, ErrorPrototype, ErrorConstructor, void) \
|
|
|
|
__JS_ENUMERATE(FinalizationRegistry, finalization_registry, FinalizationRegistryPrototype, FinalizationRegistryConstructor, void) \
|
|
|
|
__JS_ENUMERATE(FunctionObject, function, FunctionPrototype, FunctionConstructor, void) \
|
|
|
|
__JS_ENUMERATE(GeneratorFunction, generator_function, GeneratorFunctionPrototype, GeneratorFunctionConstructor, void) \
|
|
|
|
__JS_ENUMERATE(Map, map, MapPrototype, MapConstructor, void) \
|
|
|
|
__JS_ENUMERATE(NumberObject, number, NumberPrototype, NumberConstructor, void) \
|
|
|
|
__JS_ENUMERATE(Object, object, ObjectPrototype, ObjectConstructor, void) \
|
|
|
|
__JS_ENUMERATE(Promise, promise, PromisePrototype, PromiseConstructor, void) \
|
|
|
|
__JS_ENUMERATE(RegExpObject, regexp, RegExpPrototype, RegExpConstructor, void) \
|
|
|
|
__JS_ENUMERATE(Set, set, SetPrototype, SetConstructor, void) \
|
|
|
|
__JS_ENUMERATE(ShadowRealm, shadow_realm, ShadowRealmPrototype, ShadowRealmConstructor, void) \
|
|
|
|
__JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor, void) \
|
|
|
|
__JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void) \
|
|
|
|
__JS_ENUMERATE(WeakMap, weak_map, WeakMapPrototype, WeakMapConstructor, void) \
|
|
|
|
__JS_ENUMERATE(WeakRef, weak_ref, WeakRefPrototype, WeakRefConstructor, void) \
|
2021-06-09 16:23:04 +00:00
|
|
|
__JS_ENUMERATE(WeakSet, weak_set, WeakSetPrototype, WeakSetConstructor, void)
|
2020-12-01 20:05:25 +00:00
|
|
|
|
2020-12-02 00:23:40 +00:00
|
|
|
#define JS_ENUMERATE_NATIVE_OBJECTS \
|
|
|
|
JS_ENUMERATE_NATIVE_OBJECTS_EXCLUDING_TEMPLATES \
|
|
|
|
__JS_ENUMERATE(TypedArray, typed_array, TypedArrayPrototype, TypedArrayConstructor, void)
|
|
|
|
|
2022-08-26 21:25:50 +00:00
|
|
|
#define JS_ENUMERATE_NATIVE_ERRORS \
|
|
|
|
__JS_ENUMERATE(EvalError, eval_error, EvalErrorPrototype, EvalErrorConstructor, void) \
|
|
|
|
__JS_ENUMERATE(InternalError, internal_error, InternalErrorPrototype, InternalErrorConstructor, void) \
|
|
|
|
__JS_ENUMERATE(RangeError, range_error, RangeErrorPrototype, RangeErrorConstructor, void) \
|
|
|
|
__JS_ENUMERATE(ReferenceError, reference_error, ReferenceErrorPrototype, ReferenceErrorConstructor, void) \
|
|
|
|
__JS_ENUMERATE(SyntaxError, syntax_error, SyntaxErrorPrototype, SyntaxErrorConstructor, void) \
|
|
|
|
__JS_ENUMERATE(TypeError, type_error, TypeErrorPrototype, TypeErrorConstructor, void) \
|
2020-12-01 20:05:25 +00:00
|
|
|
__JS_ENUMERATE(URIError, uri_error, URIErrorPrototype, URIErrorConstructor, void)
|
|
|
|
|
2021-05-22 19:03:26 +00:00
|
|
|
#define JS_ENUMERATE_TYPED_ARRAYS \
|
|
|
|
__JS_ENUMERATE(Uint8Array, uint8_array, Uint8ArrayPrototype, Uint8ArrayConstructor, u8) \
|
|
|
|
__JS_ENUMERATE(Uint8ClampedArray, uint8_clamped_array, Uint8ClampedArrayPrototype, Uint8ClampedArrayConstructor, ClampedU8) \
|
|
|
|
__JS_ENUMERATE(Uint16Array, uint16_array, Uint16ArrayPrototype, Uint16ArrayConstructor, u16) \
|
|
|
|
__JS_ENUMERATE(Uint32Array, uint32_array, Uint32ArrayPrototype, Uint32ArrayConstructor, u32) \
|
2021-06-17 00:37:23 +00:00
|
|
|
__JS_ENUMERATE(BigUint64Array, big_uint64_array, BigUint64ArrayPrototype, BigUint64ArrayConstructor, u64) \
|
2021-05-22 19:03:26 +00:00
|
|
|
__JS_ENUMERATE(Int8Array, int8_array, Int8ArrayPrototype, Int8ArrayConstructor, i8) \
|
|
|
|
__JS_ENUMERATE(Int16Array, int16_array, Int16ArrayPrototype, Int16ArrayConstructor, i16) \
|
|
|
|
__JS_ENUMERATE(Int32Array, int32_array, Int32ArrayPrototype, Int32ArrayConstructor, i32) \
|
2021-06-17 00:37:23 +00:00
|
|
|
__JS_ENUMERATE(BigInt64Array, big_int64_array, BigInt64ArrayPrototype, BigInt64ArrayConstructor, i64) \
|
2021-05-22 19:03:26 +00:00
|
|
|
__JS_ENUMERATE(Float32Array, float32_array, Float32ArrayPrototype, Float32ArrayConstructor, float) \
|
2020-12-05 22:28:10 +00:00
|
|
|
__JS_ENUMERATE(Float64Array, float64_array, Float64ArrayPrototype, Float64ArrayConstructor, double)
|
2020-04-10 12:06:52 +00:00
|
|
|
|
2022-01-29 21:47:29 +00:00
|
|
|
#define JS_ENUMERATE_INTL_OBJECTS \
|
|
|
|
__JS_ENUMERATE(Collator, collator, CollatorPrototype, CollatorConstructor) \
|
|
|
|
__JS_ENUMERATE(DateTimeFormat, date_time_format, DateTimeFormatPrototype, DateTimeFormatConstructor) \
|
|
|
|
__JS_ENUMERATE(DisplayNames, display_names, DisplayNamesPrototype, DisplayNamesConstructor) \
|
2022-06-29 21:58:40 +00:00
|
|
|
__JS_ENUMERATE(DurationFormat, duration_format, DurationFormatPrototype, DurationFormatConstructor) \
|
2022-01-29 21:47:29 +00:00
|
|
|
__JS_ENUMERATE(ListFormat, list_format, ListFormatPrototype, ListFormatConstructor) \
|
|
|
|
__JS_ENUMERATE(Locale, locale, LocalePrototype, LocaleConstructor) \
|
|
|
|
__JS_ENUMERATE(NumberFormat, number_format, NumberFormatPrototype, NumberFormatConstructor) \
|
|
|
|
__JS_ENUMERATE(PluralRules, plural_rules, PluralRulesPrototype, PluralRulesConstructor) \
|
|
|
|
__JS_ENUMERATE(RelativeTimeFormat, relative_time_format, RelativeTimeFormatPrototype, RelativeTimeFormatConstructor) \
|
|
|
|
__JS_ENUMERATE(Segmenter, segmenter, SegmenterPrototype, SegmenterConstructor)
|
2021-08-08 17:35:29 +00:00
|
|
|
|
2021-08-07 21:40:32 +00:00
|
|
|
#define JS_ENUMERATE_TEMPORAL_OBJECTS \
|
|
|
|
__JS_ENUMERATE(Calendar, calendar, CalendarPrototype, CalendarConstructor) \
|
|
|
|
__JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor) \
|
|
|
|
__JS_ENUMERATE(Instant, instant, InstantPrototype, InstantConstructor) \
|
|
|
|
__JS_ENUMERATE(PlainDate, plain_date, PlainDatePrototype, PlainDateConstructor) \
|
|
|
|
__JS_ENUMERATE(PlainDateTime, plain_date_time, PlainDateTimePrototype, PlainDateTimeConstructor) \
|
2021-08-14 22:54:24 +00:00
|
|
|
__JS_ENUMERATE(PlainMonthDay, plain_month_day, PlainMonthDayPrototype, PlainMonthDayConstructor) \
|
2021-08-07 21:40:32 +00:00
|
|
|
__JS_ENUMERATE(PlainTime, plain_time, PlainTimePrototype, PlainTimeConstructor) \
|
|
|
|
__JS_ENUMERATE(PlainYearMonth, plain_year_month, PlainYearMonthPrototype, PlainYearMonthConstructor) \
|
|
|
|
__JS_ENUMERATE(TimeZone, time_zone, TimeZonePrototype, TimeZoneConstructor) \
|
2021-08-01 16:20:11 +00:00
|
|
|
__JS_ENUMERATE(ZonedDateTime, zoned_date_time, ZonedDateTimePrototype, ZonedDateTimeConstructor)
|
2021-07-06 19:39:55 +00:00
|
|
|
|
2022-08-26 23:54:55 +00:00
|
|
|
#define JS_ENUMERATE_BUILTIN_NAMESPACE_OBJECTS \
|
|
|
|
__JS_ENUMERATE(AtomicsObject, atomics) \
|
2022-08-28 13:16:32 +00:00
|
|
|
__JS_ENUMERATE(ConsoleObject, console) \
|
2022-08-26 23:54:55 +00:00
|
|
|
__JS_ENUMERATE(Intl::Intl, intl) \
|
|
|
|
__JS_ENUMERATE(JSONObject, json) \
|
|
|
|
__JS_ENUMERATE(MathObject, math) \
|
|
|
|
__JS_ENUMERATE(ReflectObject, reflect) \
|
|
|
|
__JS_ENUMERATE(Temporal::Temporal, temporal)
|
|
|
|
|
2021-07-15 12:58:27 +00:00
|
|
|
#define JS_ENUMERATE_ITERATOR_PROTOTYPES \
|
|
|
|
__JS_ENUMERATE(Iterator, iterator) \
|
|
|
|
__JS_ENUMERATE(ArrayIterator, array_iterator) \
|
2021-11-23 13:53:02 +00:00
|
|
|
__JS_ENUMERATE(AsyncIterator, async_iterator) \
|
2022-01-30 00:18:47 +00:00
|
|
|
__JS_ENUMERATE(Intl::SegmentIterator, intl_segment_iterator) \
|
2021-07-15 12:58:27 +00:00
|
|
|
__JS_ENUMERATE(MapIterator, map_iterator) \
|
|
|
|
__JS_ENUMERATE(RegExpStringIterator, regexp_string_iterator) \
|
|
|
|
__JS_ENUMERATE(SetIterator, set_iterator) \
|
2020-07-12 03:23:01 +00:00
|
|
|
__JS_ENUMERATE(StringIterator, string_iterator)
|
2020-07-09 21:58:20 +00:00
|
|
|
|
2020-04-10 12:06:52 +00:00
|
|
|
#define JS_ENUMERATE_BUILTIN_TYPES \
|
|
|
|
JS_ENUMERATE_NATIVE_OBJECTS \
|
2021-06-11 16:54:42 +00:00
|
|
|
JS_ENUMERATE_NATIVE_ERRORS \
|
2020-12-01 20:05:25 +00:00
|
|
|
JS_ENUMERATE_TYPED_ARRAYS
|
2020-04-10 10:42:33 +00:00
|
|
|
|
2020-09-18 07:49:51 +00:00
|
|
|
#define JS_ENUMERATE_WELL_KNOWN_SYMBOLS \
|
|
|
|
__JS_ENUMERATE(iterator, iterator) \
|
|
|
|
__JS_ENUMERATE(asyncIterator, async_iterator) \
|
|
|
|
__JS_ENUMERATE(match, match) \
|
|
|
|
__JS_ENUMERATE(matchAll, match_all) \
|
|
|
|
__JS_ENUMERATE(replace, replace) \
|
2021-07-01 20:28:33 +00:00
|
|
|
__JS_ENUMERATE(replaceAll, replace_all) \
|
2020-09-18 07:49:51 +00:00
|
|
|
__JS_ENUMERATE(search, search) \
|
|
|
|
__JS_ENUMERATE(split, split) \
|
|
|
|
__JS_ENUMERATE(hasInstance, has_instance) \
|
|
|
|
__JS_ENUMERATE(isConcatSpreadable, is_concat_spreadable) \
|
|
|
|
__JS_ENUMERATE(unscopables, unscopables) \
|
|
|
|
__JS_ENUMERATE(species, species) \
|
|
|
|
__JS_ENUMERATE(toPrimitive, to_primitive) \
|
2020-07-09 22:34:20 +00:00
|
|
|
__JS_ENUMERATE(toStringTag, to_string_tag)
|
|
|
|
|
2022-07-16 05:44:03 +00:00
|
|
|
#define JS_ENUMERATE_REGEXP_FLAGS \
|
|
|
|
__JS_ENUMERATE(hasIndices, has_indices, d) \
|
|
|
|
__JS_ENUMERATE(global, global, g) \
|
|
|
|
__JS_ENUMERATE(ignoreCase, ignore_case, i) \
|
|
|
|
__JS_ENUMERATE(multiline, multiline, m) \
|
|
|
|
__JS_ENUMERATE(dotAll, dot_all, s) \
|
|
|
|
__JS_ENUMERATE(unicodeSets, unicode_sets, v) \
|
|
|
|
__JS_ENUMERATE(unicode, unicode, u) \
|
2021-07-09 19:39:43 +00:00
|
|
|
__JS_ENUMERATE(sticky, sticky, y)
|
2020-11-27 23:04:01 +00:00
|
|
|
|
2020-03-07 18:42:11 +00:00
|
|
|
namespace JS {
|
|
|
|
|
|
|
|
class ASTNode;
|
2021-06-21 22:55:03 +00:00
|
|
|
class Accessor;
|
2022-11-23 10:44:43 +00:00
|
|
|
struct AsyncGeneratorRequest;
|
2020-06-06 00:14:10 +00:00
|
|
|
class BigInt;
|
2020-04-19 20:03:02 +00:00
|
|
|
class BoundFunction;
|
2020-03-08 18:23:58 +00:00
|
|
|
class Cell;
|
2021-06-21 22:55:03 +00:00
|
|
|
class CellAllocator;
|
2021-06-30 18:42:13 +00:00
|
|
|
class ClassExpression;
|
2022-04-19 22:06:45 +00:00
|
|
|
struct ClassFieldDefinition;
|
LibJS: Add a JS::Completion class and JS::ThrowCompletionOr<T> template
We decided that we want to move away from throwing exceptions in AOs
and regular functions implicitly and then returning some
default-constructed value (usually an empty JS::Value) - this requires
remembering to check for an exception at the call site, which is
error-prone. It's also awkward for return values that cannot be
default-constructed, e.g. MarkedValueList.
Instead, the thrown value should be part of the function return value.
The solution to this is moving closer to the spec and using something
they call "completion records":
https://tc39.es/ecma262/#sec-completion-record-specification-type
This has various advantages:
- It becomes crystal clear whether some AO can fail or not, and errors
need to be handled and values unwrapped explicitly (for that reason
compatibility with the TRY() macro is already built-in, and a similar
TRY_OR_DISCARD() macro has been added specifically for use in LibJS,
while the majority of functions doesn't return ThrowCompletionOr yet)
- We no longer need to mix "valid" and "invalid" values of various types
for the success and exception outcomes (e.g. null/non-null AK::String,
empty/non-empty JS::Value)
- Subsequently it's no longer possible to accidentally use an exception
outcome return value as a success outcome return value (e.g. any AO
that returns a numeric type would return 0 even after throwing an
exception, at least before we started making use of Optional for that)
- Eventually the VM will no longer need to store an exception, and
temporarily clearing an exception (e.g. to call a function) becomes
obsolete - instead, completions will simply propagate up to the caller
outside of LibJS, which then can deal with it in any way
- Similar to throw we'll be able to implement the functionality of
break, continue, and return using completions, which will lead to
easier to understand code and fewer workarounds - the current
unwinding mechanism is not even remotely similar to the spec's
approach
The spec's NormalCompletion and ThrowCompletion AOs have been
implemented as simple wrappers around the JS::Completion constructor.
UpdateEmpty has been implemented as a JS::Completion method.
There's also a new VM::throw_completion<T>() helper, which basically
works like VM::throw_exception<T>() - it creates a T object (usually a
JS::Error), and returns it wrapped in a JS::Completion of Type::Throw.
Two temporary usage patterns have emerged:
1. Callee already returns ThrowCompletionOr, but caller doesn't:
auto foo = TRY_OR_DISCARD(bar());
2. Caller already returns ThrowCompletionOr, but callee doesn't:
auto foo = bar();
if (auto* exception = vm.exception())
return throw_completion(exception->value());
Eventually all error handling and unwrapping can be done with just TRY()
or possibly even operator? in the future :^)
Co-authored-by: Andreas Kling <kling@serenityos.org>
2021-09-15 19:46:51 +00:00
|
|
|
class Completion;
|
2020-09-29 19:15:06 +00:00
|
|
|
class Console;
|
2021-07-01 10:24:46 +00:00
|
|
|
class DeclarativeEnvironment;
|
2020-04-19 09:30:47 +00:00
|
|
|
class DeferGC;
|
2021-09-24 22:02:04 +00:00
|
|
|
class ECMAScriptFunctionObject;
|
2021-07-01 10:24:46 +00:00
|
|
|
class Environment;
|
2020-03-24 13:37:39 +00:00
|
|
|
class Error;
|
2021-06-09 22:27:01 +00:00
|
|
|
class ErrorType;
|
2021-10-14 15:12:53 +00:00
|
|
|
struct ExecutionContext;
|
2022-11-23 11:16:51 +00:00
|
|
|
struct ExportEntry;
|
|
|
|
class ExportStatement;
|
2020-03-11 18:27:43 +00:00
|
|
|
class Expression;
|
2022-11-23 11:05:07 +00:00
|
|
|
class ForStatement;
|
2021-07-01 10:24:46 +00:00
|
|
|
class FunctionEnvironment;
|
2021-06-09 22:49:23 +00:00
|
|
|
class FunctionNode;
|
2021-07-01 10:24:46 +00:00
|
|
|
class GlobalEnvironment;
|
2020-04-01 19:04:42 +00:00
|
|
|
class GlobalObject;
|
2020-03-18 19:03:17 +00:00
|
|
|
class HandleImpl;
|
2020-03-08 18:23:58 +00:00
|
|
|
class Heap;
|
|
|
|
class HeapBlock;
|
2022-11-23 11:16:51 +00:00
|
|
|
struct ImportEntry;
|
|
|
|
class ImportStatement;
|
2020-03-07 18:42:11 +00:00
|
|
|
class Interpreter;
|
2022-08-26 23:54:55 +00:00
|
|
|
class Intrinsics;
|
2022-01-17 13:48:22 +00:00
|
|
|
class Module;
|
2021-01-18 07:33:41 +00:00
|
|
|
class NativeFunction;
|
2021-07-01 10:24:46 +00:00
|
|
|
class ObjectEnvironment;
|
2022-11-23 11:39:23 +00:00
|
|
|
class Parser;
|
|
|
|
struct ParserError;
|
2020-03-11 17:55:01 +00:00
|
|
|
class PrimitiveString;
|
2022-10-02 11:11:30 +00:00
|
|
|
class PromiseCapability;
|
LibJS: Add initial support for Promises
Almost a year after first working on this, it's finally done: an
implementation of Promises for LibJS! :^)
The core functionality is working and closely following the spec [1].
I mostly took the pseudo code and transformed it into C++ - if you read
and understand it, you will know how the spec implements Promises; and
if you read the spec first, the code will look very familiar.
Implemented functions are:
- Promise() constructor
- Promise.prototype.then()
- Promise.prototype.catch()
- Promise.prototype.finally()
- Promise.resolve()
- Promise.reject()
For the tests I added a new function to test-js's global object,
runQueuedPromiseJobs(), which calls vm.run_queued_promise_jobs().
By design, queued jobs normally only run after the script was fully
executed, making it improssible to test handlers in individual test()
calls by default [2].
Subsequent commits include integrations into LibWeb and js(1) -
pretty-printing, running queued promise jobs when necessary.
This has an unusual amount of dbgln() statements, all hidden behind the
PROMISE_DEBUG flag - I'm leaving them in for now as they've been very
useful while debugging this, things can get quite complex with so many
asynchronously executed functions.
I've not extensively explored use of these APIs for promise-based
functionality in LibWeb (fetch(), Notification.requestPermission()
etc.), but we'll get there in due time.
[1]: https://tc39.es/ecma262/#sec-promise-objects
[2]: https://tc39.es/ecma262/#sec-jobs-and-job-queues
2021-04-01 20:13:29 +00:00
|
|
|
class PromiseReaction;
|
LibJS: Add new PropertyDescriptor class and related abstract operations
This is an implementation of 'The Property Descriptor Specification
Type' and related abstract operations, namely:
- IsAccessorDescriptor
- IsDataDescriptor
- IsGenericDescriptor
- FromPropertyDescriptor
- ToPropertyDescriptor
- CompletePropertyDescriptor
It works with Optional<T> to enable omitting certain fields, which will
eventually replace the Attribute::Has{Getter,Setter,Configurable,
Enumerable,Writable} bit flags, which are awkward to work with - being
able to use an initializer list with any of the possible attributes is
much more convenient.
Parts of the current PropertyAttributes implementation as well as the
much simpler PropertyDescriptor struct in Object.h will eventually be
replaced with this and completely go away.
Property storage will still use the PropertyAttributes bit flags, this
is for the layers above.
Note that this is currently guarded behind an #if 0 as if conflicts with
the existing PropertyDescriptor struct, but it's known to compile and
work just fine - I simply want to have this in a separate commit, the
primary object rewrite commit will be large enough as is.
2021-07-03 21:57:21 +00:00
|
|
|
class PropertyAttributes;
|
|
|
|
class PropertyDescriptor;
|
2021-10-24 14:01:24 +00:00
|
|
|
class PropertyKey;
|
2021-09-11 18:20:07 +00:00
|
|
|
class Realm;
|
2020-04-27 10:10:16 +00:00
|
|
|
class Reference;
|
2020-03-07 18:42:11 +00:00
|
|
|
class ScopeNode;
|
2022-01-17 13:48:22 +00:00
|
|
|
class Script;
|
2020-04-02 17:32:21 +00:00
|
|
|
class Shape;
|
2020-03-23 15:46:41 +00:00
|
|
|
class Statement;
|
2021-06-05 12:22:11 +00:00
|
|
|
class StringOrSymbol;
|
LibJS: Reduce AST memory usage by shrink-wrapping source range info
Before this change, each AST node had a 64-byte SourceRange member.
This SourceRange had the following layout:
filename: StringView (16 bytes)
start: Position (24 bytes)
end: Position (24 bytes)
The Position structs have { line, column, offset }, all members size_t.
To reduce memory consumption, AST nodes now only store the following:
source_code: NonnullRefPtr<SourceCode> (8 bytes)
start_offset: u32 (4 bytes)
end_offset: u32 (4 bytes)
SourceCode is a new ref-counted data structure that keeps the filename
and original parsed source code in a single location, and all AST nodes
have a pointer to it.
The start_offset and end_offset can be turned into (line, column) when
necessary by calling SourceCode::range_from_offsets(). This will walk
the source code string and compute line/column numbers on the fly, so
it's not necessarily fast, but it should be rare since this information
is primarily used for diagnostics and exception stack traces.
With this, ASTNode shrinks from 80 bytes to 32 bytes. This gives us a
~23% reduction in memory usage when loading twitter.com/awesomekling
(330 MiB before, 253 MiB after!) :^)
2022-11-21 16:37:38 +00:00
|
|
|
class SourceCode;
|
|
|
|
struct SourceRange;
|
2022-01-18 18:14:25 +00:00
|
|
|
class SourceTextModule;
|
2020-04-30 06:25:21 +00:00
|
|
|
class Symbol;
|
2020-05-25 19:24:46 +00:00
|
|
|
class Token;
|
2021-08-09 12:58:31 +00:00
|
|
|
class Utf16String;
|
2020-09-20 17:24:44 +00:00
|
|
|
class VM;
|
2020-03-07 18:42:11 +00:00
|
|
|
class Value;
|
2021-06-12 02:23:33 +00:00
|
|
|
class WeakContainer;
|
2021-10-13 20:08:48 +00:00
|
|
|
class WrappedFunction;
|
2020-04-08 09:59:18 +00:00
|
|
|
enum class DeclarationKind;
|
LibJS: Add initial support for Promises
Almost a year after first working on this, it's finally done: an
implementation of Promises for LibJS! :^)
The core functionality is working and closely following the spec [1].
I mostly took the pseudo code and transformed it into C++ - if you read
and understand it, you will know how the spec implements Promises; and
if you read the spec first, the code will look very familiar.
Implemented functions are:
- Promise() constructor
- Promise.prototype.then()
- Promise.prototype.catch()
- Promise.prototype.finally()
- Promise.resolve()
- Promise.reject()
For the tests I added a new function to test-js's global object,
runQueuedPromiseJobs(), which calls vm.run_queued_promise_jobs().
By design, queued jobs normally only run after the script was fully
executed, making it improssible to test handlers in individual test()
calls by default [2].
Subsequent commits include integrations into LibWeb and js(1) -
pretty-printing, running queued promise jobs when necessary.
This has an unusual amount of dbgln() statements, all hidden behind the
PROMISE_DEBUG flag - I'm leaving them in for now as they've been very
useful while debugging this, things can get quite complex with so many
asynchronously executed functions.
I've not extensively explored use of these APIs for promise-based
functionality in LibWeb (fetch(), Notification.requestPermission()
etc.), but we'll get there in due time.
[1]: https://tc39.es/ecma262/#sec-promise-objects
[2]: https://tc39.es/ecma262/#sec-jobs-and-job-queues
2021-04-01 20:13:29 +00:00
|
|
|
struct AlreadyResolved;
|
|
|
|
struct JobCallback;
|
2022-01-18 18:14:25 +00:00
|
|
|
struct ModuleRequest;
|
2020-03-07 18:42:11 +00:00
|
|
|
|
2020-11-30 22:50:43 +00:00
|
|
|
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype
|
|
|
|
class ProxyObject;
|
|
|
|
class ProxyConstructor;
|
|
|
|
|
2021-06-15 07:04:08 +00:00
|
|
|
// Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct constructor
|
2021-11-23 14:01:35 +00:00
|
|
|
class AsyncFromSyncIteratorPrototype;
|
2022-05-05 06:47:36 +00:00
|
|
|
class AsyncGenerator;
|
|
|
|
class AsyncGeneratorPrototype;
|
|
|
|
class GeneratorPrototype;
|
2021-06-15 07:04:08 +00:00
|
|
|
|
2020-12-02 00:23:40 +00:00
|
|
|
class TypedArrayConstructor;
|
|
|
|
class TypedArrayPrototype;
|
|
|
|
|
2022-08-26 23:54:55 +00:00
|
|
|
class AtomicsObject;
|
2022-08-28 13:16:32 +00:00
|
|
|
class ConsoleObject;
|
2022-08-26 23:54:55 +00:00
|
|
|
class JSONObject;
|
|
|
|
class MathObject;
|
|
|
|
class ReflectObject;
|
|
|
|
|
2021-05-22 19:03:26 +00:00
|
|
|
// Tag type used to differentiate between u8 as used by Uint8Array and u8 as used by Uint8ClampedArray.
|
|
|
|
struct ClampedU8;
|
|
|
|
|
2020-12-01 20:05:25 +00:00
|
|
|
#define __JS_ENUMERATE(ClassName, snake_name, ConstructorName, PrototypeName, ArrayType) \
|
|
|
|
class ClassName; \
|
|
|
|
class ConstructorName; \
|
2020-04-10 12:06:52 +00:00
|
|
|
class PrototypeName;
|
2020-12-02 00:23:40 +00:00
|
|
|
JS_ENUMERATE_NATIVE_OBJECTS_EXCLUDING_TEMPLATES
|
2021-06-11 16:54:42 +00:00
|
|
|
JS_ENUMERATE_NATIVE_ERRORS
|
2020-12-02 00:23:40 +00:00
|
|
|
JS_ENUMERATE_TYPED_ARRAYS
|
2020-04-10 12:06:52 +00:00
|
|
|
#undef __JS_ENUMERATE
|
2020-04-10 10:42:33 +00:00
|
|
|
|
2022-08-26 23:54:55 +00:00
|
|
|
#define __JS_ENUMERATE(ClassName, snake_name) \
|
|
|
|
class ClassName; \
|
|
|
|
JS_ENUMERATE_BUILTIN_NAMESPACE_OBJECTS
|
|
|
|
#undef __JS_ENUMERATE
|
|
|
|
|
2021-08-08 17:35:29 +00:00
|
|
|
namespace Intl {
|
|
|
|
#define __JS_ENUMERATE(ClassName, snake_name, ConstructorName, PrototypeName) \
|
|
|
|
class ClassName; \
|
|
|
|
class ConstructorName; \
|
|
|
|
class PrototypeName;
|
|
|
|
JS_ENUMERATE_INTL_OBJECTS
|
|
|
|
#undef __JS_ENUMERATE
|
2022-01-29 23:08:42 +00:00
|
|
|
|
2022-08-26 23:54:55 +00:00
|
|
|
class Intl;
|
2022-07-19 16:07:00 +00:00
|
|
|
class MathematicalValue;
|
|
|
|
|
2022-01-29 23:08:42 +00:00
|
|
|
// Not included in JS_ENUMERATE_INTL_OBJECTS due to missing distinct constructor
|
|
|
|
class Segments;
|
|
|
|
class SegmentsPrototype;
|
2021-08-08 17:35:29 +00:00
|
|
|
};
|
|
|
|
|
2021-07-06 19:39:55 +00:00
|
|
|
namespace Temporal {
|
|
|
|
#define __JS_ENUMERATE(ClassName, snake_name, ConstructorName, PrototypeName) \
|
|
|
|
class ClassName; \
|
|
|
|
class ConstructorName; \
|
|
|
|
class PrototypeName;
|
|
|
|
JS_ENUMERATE_TEMPORAL_OBJECTS
|
|
|
|
#undef __JS_ENUMERATE
|
2022-08-26 23:54:55 +00:00
|
|
|
class Temporal;
|
2022-03-09 21:59:17 +00:00
|
|
|
struct DurationRecord;
|
|
|
|
struct DateDurationRecord;
|
|
|
|
struct TimeDurationRecord;
|
|
|
|
struct PartialDurationRecord;
|
2021-07-06 19:39:55 +00:00
|
|
|
};
|
|
|
|
|
LibJS: Add a JS::Completion class and JS::ThrowCompletionOr<T> template
We decided that we want to move away from throwing exceptions in AOs
and regular functions implicitly and then returning some
default-constructed value (usually an empty JS::Value) - this requires
remembering to check for an exception at the call site, which is
error-prone. It's also awkward for return values that cannot be
default-constructed, e.g. MarkedValueList.
Instead, the thrown value should be part of the function return value.
The solution to this is moving closer to the spec and using something
they call "completion records":
https://tc39.es/ecma262/#sec-completion-record-specification-type
This has various advantages:
- It becomes crystal clear whether some AO can fail or not, and errors
need to be handled and values unwrapped explicitly (for that reason
compatibility with the TRY() macro is already built-in, and a similar
TRY_OR_DISCARD() macro has been added specifically for use in LibJS,
while the majority of functions doesn't return ThrowCompletionOr yet)
- We no longer need to mix "valid" and "invalid" values of various types
for the success and exception outcomes (e.g. null/non-null AK::String,
empty/non-empty JS::Value)
- Subsequently it's no longer possible to accidentally use an exception
outcome return value as a success outcome return value (e.g. any AO
that returns a numeric type would return 0 even after throwing an
exception, at least before we started making use of Optional for that)
- Eventually the VM will no longer need to store an exception, and
temporarily clearing an exception (e.g. to call a function) becomes
obsolete - instead, completions will simply propagate up to the caller
outside of LibJS, which then can deal with it in any way
- Similar to throw we'll be able to implement the functionality of
break, continue, and return using completions, which will lead to
easier to understand code and fewer workarounds - the current
unwinding mechanism is not even remotely similar to the spec's
approach
The spec's NormalCompletion and ThrowCompletion AOs have been
implemented as simple wrappers around the JS::Completion constructor.
UpdateEmpty has been implemented as a JS::Completion method.
There's also a new VM::throw_completion<T>() helper, which basically
works like VM::throw_exception<T>() - it creates a T object (usually a
JS::Error), and returns it wrapped in a JS::Completion of Type::Throw.
Two temporary usage patterns have emerged:
1. Callee already returns ThrowCompletionOr, but caller doesn't:
auto foo = TRY_OR_DISCARD(bar());
2. Caller already returns ThrowCompletionOr, but callee doesn't:
auto foo = bar();
if (auto* exception = vm.exception())
return throw_completion(exception->value());
Eventually all error handling and unwrapping can be done with just TRY()
or possibly even operator? in the future :^)
Co-authored-by: Andreas Kling <kling@serenityos.org>
2021-09-15 19:46:51 +00:00
|
|
|
template<typename T>
|
|
|
|
class ThrowCompletionOr;
|
|
|
|
|
2020-03-18 19:03:17 +00:00
|
|
|
template<class T>
|
|
|
|
class Handle;
|
|
|
|
|
LibJS: Let MarkedVector<T> inherit from Vector and handle Cell* + Value
Note: MarkedVector is still relatively new and has zero users right now,
so these changes don't affect any code other than the class itself.
Reasons for this are the rather limited API:
- Despite the name and unlike MarkedValueList, MarkedVector isn't
actually a Vector, it *wraps* a Vector. This means that plenty of
convenient APIs are unavailable and have to be exported on the class
separately and forwarded to the internal Vector, or need to go through
the exposed Span - both not great options.
- Exposing append(Cell*) and prepend(Cell*) on the base class means that
it was possible to append any Cell type, not just T! All the strong
typing guarantees are basically gone, and MarkedVector doesn't do much
more than casting Cells to the appropriate type through the exposed
Span.
All of this combined means that MarkedVector - in its current form -
doesn't provide much value over MarkedValueList, and that we have to
maintain two separate, yet almost identical classes.
Let's fix this!
The updated MarkedVector steals various concepts from the existing
MarkedValueList, especially the ability to copy. On the other hand, it
remains generic enough to handle both Cell* and Value for T, making
MarkedValueList effectively redundant :^)
Additionally, by inheriting from Vector we get all the current and
future APIs without having to select and expose them separately.
MarkedVectorBase remains and takes care of communicating creation and
destruction of the class to the heap. Visiting the contained values is
handled via a pure virtual method gather_roots(), which is being called
by the Heap's function of the same name; much like the VM has one.
From there, values are added to the roots HashTable if they are cells
for T = Value, and unconditionally for any other T.
As a small additional improvement the template now also takes an
inline_capacity parameter, defaulting to 32, and forwards it to the
Vector template; allowing for possible future optimizations of current
uses of MarkedValueList, which hard-codes it to 32.
2022-02-09 09:40:49 +00:00
|
|
|
template<class T, size_t inline_capacity = 32>
|
2021-12-16 17:54:06 +00:00
|
|
|
class MarkedVector;
|
|
|
|
|
2021-06-03 08:46:30 +00:00
|
|
|
namespace Bytecode {
|
2021-06-09 02:19:58 +00:00
|
|
|
class BasicBlock;
|
2021-06-09 08:02:01 +00:00
|
|
|
struct Executable;
|
2021-06-03 08:46:30 +00:00
|
|
|
class Generator;
|
|
|
|
class Instruction;
|
|
|
|
class Interpreter;
|
|
|
|
class Register;
|
|
|
|
}
|
|
|
|
|
2020-03-07 18:42:11 +00:00
|
|
|
}
|