Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions scripts/emitted-baseline-runtime.txt
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ instanceof-class-union-through-nested-record-views.runtime.ts.cpp 540bfb9a5f2f
instanceof-date-over-a-union-of-native-carriers.runtime.ts.cpp 6543d6b77454
instanceof-derived-class.ts.cpp 526f685a24d1
instanceof-else-arm-narrowed-to-never-is-not-converted.runtime.ts.cpp 439d10677a1d
instanceof-error-over-boxed-error-subclass.runtime.ts.cpp 0a6f5f7e88c3
instanceof-error-over-error-subclass-union-arm.runtime.ts.cpp 3f5bdc34a912
instanceof-error-over-union-carrier.ts.cpp c0a862a2df14
instanceof-over-union-with-record-arm-after-a-boxed-callable.runtime.ts.cpp 61940b54d14f
Expand Down Expand Up @@ -946,6 +947,7 @@ object-integrity-uses-physical-index-protocol.runtime.ts.cpp 23551c4185ed
object-is-over-union-and-optional-carriers.runtime.ts.cpp 199c1035ac10
object-key-enumeration-requires-native-object.runtime.ts.cpp f6c110078688
object-keyed-map-index-matches-node.runtime.ts.cpp ab5c357ecdd6
object-keys-over-recursive-dictionary-or-array-union.runtime.ts.cpp 061e846e55a7
object-literal-beside-class-in-or-keeps-its-own-layout.runtime.ts.cpp d86094099c83
object-literal-empty-array-filled-through-untyped-computed-read.runtime.js.cpp d13b67ac0d25
object-literal-es5-method-receiver.runtime.js.cpp 4c1c19bfe47a
Expand Down Expand Up @@ -1229,6 +1231,7 @@ string-length-reuse-mutation.ts.cpp 0cd264bd8d0a
string-length-reuse.ts.cpp 429819fd086c
string-normalize-forms.ts.cpp 98c5bc66ab2f
string-object-with-declared-fields.ts.cpp 8a8056fbe5ad
string-of-boxed-error-subclass-overriding-tostring.runtime.ts.cpp 5bc5135e23d8
string-of-symbol.runtime.js.cpp 234cbfea9b0d
string-raw-tagged-template-into-regexp.runtime.ts.cpp b72805f3b232
string-shared-layout.ts.cpp 645b4b7ef4b6
Expand Down
53 changes: 48 additions & 5 deletions src/targets/cpp/runtime/gea_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -15981,6 +15981,15 @@ struct ArrayObjectElementOf<ArrayObject<Element>> {
using type = Element;
};

namespace detail {
template <typename Element>
ArrayObject<Element> arrayObjectBaseProbe(const ArrayObject<Element>&);
template <typename T>
using ArrayObjectBaseOf = decltype(arrayObjectBaseProbe(std::declval<const T&>()));
template <typename T>
concept DerivedArrayObject = !IsArrayObjectStruct<T>::value && requires { typename ArrayObjectBaseOf<T>; };
}

namespace detail {
template <typename T>
struct DynamicCarrier;
Expand Down Expand Up @@ -20247,6 +20256,8 @@ struct Error;
namespace host {
inline gea::Ref<gea::runtime::Error> createRuntimeError(const char* kind, const gea::Optional<std::string>& message);
inline bool isRuntimeError(const gea::Value& value);
inline gea::Ref<gea::runtime::Error> runtimeErrorOf(const gea::Value& value);
inline bool isErrorValue(const gea::Value& value);
inline bool instanceOfRuntimeError(const gea::Value& value, const char* kind);
inline std::string runtimeErrorString(const gea::Value& value);
[[noreturn]] inline void throwRuntimeError(const char* kind, const std::string& message);
Expand Down Expand Up @@ -32639,6 +32650,26 @@ bool assignOwnPropertiesUnorderedWith(const gea::Ref<T>& source, const gea::Ref<
}

/** Array exotic own enumerable string keys, followed by enumerable expandos. */
template <typename Element>
std::vector<std::string> arrayOwnEnumerableKeys(const gea::Ref<ArrayObject<Element>>& array);
template <typename Element>
bool arrayHasOwnEnumerableKey(const gea::Ref<ArrayObject<Element>>& array, const std::string& key);
template <typename Element>
std::vector<std::string> arrayOwnPropertyNames(const gea::Ref<ArrayObject<Element>>& array);

template <detail::DerivedArrayObject Array>
std::vector<std::string> arrayOwnEnumerableKeys(const gea::Ref<Array>& array) {
return arrayOwnEnumerableKeys(gea::Ref<detail::ArrayObjectBaseOf<Array>>(array));
}
template <detail::DerivedArrayObject Array>
bool arrayHasOwnEnumerableKey(const gea::Ref<Array>& array, const std::string& key) {
return arrayHasOwnEnumerableKey(gea::Ref<detail::ArrayObjectBaseOf<Array>>(array), key);
}
template <detail::DerivedArrayObject Array>
std::vector<std::string> arrayOwnPropertyNames(const gea::Ref<Array>& array) {
return arrayOwnPropertyNames(gea::Ref<detail::ArrayObjectBaseOf<Array>>(array));
}

template <typename Element>
std::vector<std::string> arrayOwnEnumerableKeys(const gea::Ref<ArrayObject<Element>>& array) {
std::vector<std::string> keys;
Expand Down Expand Up @@ -43246,7 +43277,7 @@ inline std::string typeOf(const gea::Value &value) {
// without that dynamic boundary still refuses by name inside `dynamicToString`:
// manufacturing "[object Object]" would bypass a possible native `toString`.
inline std::string toString(const gea::Value &value) {
if (gea::host::isRuntimeError(value)) return gea::host::runtimeErrorString(value);
if (gea::host::isErrorValue(value)) return gea::host::runtimeErrorString(value);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
switch (value.tag()) {
case gea::Value::Tag::Undefined:
return toStringUndefined();
Expand Down Expand Up @@ -44200,7 +44231,7 @@ inline void registerErrorRecordType(const char* name) {
*/
inline bool instanceOfError(const gea::Value& value, const char* name) {
if (value.tag() != gea::Value::Tag::Object) return false;
if (gea::host::isRuntimeError(value)) return gea::host::instanceOfRuntimeError(value, name);
if (gea::host::isErrorValue(value)) return gea::host::instanceOfRuntimeError(value, name);
const auto found = detail::errorPayloadTypes().find(name);
if (found == detail::errorPayloadTypes().end()) return false;
const void* payload = value.payloadType();
Expand Down Expand Up @@ -51915,11 +51946,23 @@ inline bool isRuntimeError(const gea::Value& value) {
return value.tag() == gea::Value::Tag::Object &&
value.payloadType() == gea::detail::payloadTypeTagFor<gea::Ref<gea::runtime::Error>>();
}
inline gea::Ref<gea::runtime::Error> runtimeErrorOf(const gea::Value& value) {
if (value.tag() != gea::Value::Tag::Object) return {};
if (value.payloadType() == gea::detail::payloadTypeTagFor<gea::Ref<gea::runtime::Error>>()) return value.as<gea::Ref<gea::runtime::Error>>();
if (value.classObject() && gea::detail::classIdentityExtends(value.classIdentity(), &gea::detail::RefOperationsFor<gea::runtime::Error>::table))
return value.classObject().template staticCast<gea::runtime::Error>();
return {};
}
inline bool isErrorValue(const gea::Value& value) { return static_cast<bool>(runtimeErrorOf(value)); }
inline bool instanceOfRuntimeError(const gea::Value& value, const char* kind) {
return isRuntimeError(value) && value.as<gea::Ref<gea::runtime::Error>>()->instanceOf(kind);
const auto error = runtimeErrorOf(value);
return error && error->instanceOf(kind);
}
inline std::string runtimeErrorString(const gea::Value& value) {
return gea::detail::unboxAs<gea::Ref<gea::runtime::Error>>(value, gea::Value::Tag::Object, "Error ToString")->toString();
const auto error = runtimeErrorOf(value);
if (!error) return gea::detail::unboxAs<gea::Ref<gea::runtime::Error>>(value, gea::Value::Tag::Object, "Error ToString")->toString();
if (value.classObject() && value.getProperty(gea::PropertyKey::string("toString")).tag() == gea::Value::Tag::Function) return gea::dynamicToString(value);
return error->toString();
}
[[noreturn]] inline void throwRuntimeError(const char* kind, const std::string& message) {
#if defined(GEA_RUNTIME_THROW_ENDS_PROGRAM) && GEA_RUNTIME_THROW_ENDS_PROGRAM
Expand All @@ -51944,7 +51987,7 @@ inline const bool uncaughtDescriberInstalled = [] {
std::rethrow_exception(current);
} catch (const gea::Value& value) {
try {
text = gea::host::isRuntimeError(value) ? gea::host::runtimeErrorString(value) : gea::dynamicToString(value);
text = gea::host::isErrorValue(value) ? gea::host::runtimeErrorString(value) : gea::dynamicToString(value);
} catch (...) {
text = "<a value whose ToString threw>";
}
Expand Down
40 changes: 40 additions & 0 deletions test/runtime/instanceof-error-over-boxed-error-subclass.runtime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class ParseFailure extends Error {
status: number
constructor(status: number, message: string, name = 'Error') {
super(message)
this.name = name
this.status = status
}
}

class EntityTooLarge extends ParseFailure {
constructor(limit: number) {
super(413, `request entity too large: ${limit}`, 'PayloadTooLargeError')
}
}

function fail(): never {
throw new ParseFailure(400, 'bad body', 'SyntaxError')
}

const describe = (err: unknown): string => (err instanceof Error ? `${err.name}: ${err.message}` : `other:${String(err)}`)

let caught: unknown = undefined
try {
fail()
} catch (err) {
caught = err
}

//! expect: true true SyntaxError: bad body SyntaxError: bad body
console.log(caught instanceof Error, caught instanceof ParseFailure, describe(caught), String(caught))

const deep: unknown = new EntityTooLarge(1024)
//! expect: true true true PayloadTooLargeError: request entity too large: 1024
console.log(deep instanceof Error, deep instanceof ParseFailure, deep instanceof EntityTooLarge, String(deep))

const plain: unknown = new Error('plain')
const typed: unknown = new TypeError('typed')
const record: unknown = { name: 'Error', message: 'shaped like one' }
//! expect: Error: plain TypeError: typed false other:[object Object]
console.log(describe(plain), describe(typed), record instanceof Error, describe(record))
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
type Leaf = string
type Tree = { [key: string]: Leaf | Tree | Branch }
type Branch = (Leaf | Tree | Branch | undefined)[]
type Container = Tree | Branch

const keysOf = (container: Container): string => Object.keys(container).join(',')

const tree: Tree = { a: 'x', b: ['y', { c: 'z' }] }
const branch: Branch = ['p', tree, undefined, 'q']
tree['self'] = branch

console.log(keysOf(tree))
console.log(keysOf(branch))

//! expect: a,b,self
//! expect: 0,1,2,3
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
class HttpError extends Error {
status: number
constructor(status: number, message: string) {
super(message)
this.name = 'HttpError'
this.status = status
}
toString(): string {
return `HttpError(${this.status}): ${this.message}`
}
}

class NotFound extends HttpError {
constructor(path: string) {
super(404, `no route for ${path}`)
}
}

class PlainError extends Error {
constructor(message: string) {
super(message)
this.name = 'PlainError'
}
}

const overriding: unknown = new HttpError(500, 'boom')
const inherited: unknown = new NotFound('/missing')
const plain: unknown = new PlainError('plain')

//! expect: HttpError(500): boom HttpError(500): boom
console.log(String(overriding), `${overriding}`)
//! expect: HttpError(404): no route for /missing
console.log(String(inherited))
//! expect: PlainError: plain
console.log(String(plain))

let caught: unknown = undefined
try {
throw overriding
} catch (err) {
caught = err
}
//! expect: true caught HttpError(500): boom
console.log(caught instanceof HttpError, 'caught', String(caught))
Loading