diff --git a/include/hermes/VM/PredefinedStrings.def b/include/hermes/VM/PredefinedStrings.def index 011169697ac..2fda91b02b7 100644 --- a/include/hermes/VM/PredefinedStrings.def +++ b/include/hermes/VM/PredefinedStrings.def @@ -138,6 +138,7 @@ STR(gc, "gc") STR(name, "name") STR(displayName, "displayName") STR(message, "message") +STR(cause, "cause") STR(stack, "stack") STR(stacktraceTooLong, "Stacktrace too long") #define ALL_ERROR_TYPE(name) STR(name, "" #name) diff --git a/lib/VM/JSLib/Error.cpp b/lib/VM/JSLib/Error.cpp index 6c593daef64..13cf3132d07 100644 --- a/lib/VM/JSLib/Error.cpp +++ b/lib/VM/JSLib/Error.cpp @@ -118,6 +118,38 @@ static CallResult constructErrorObject( } } + // https://tc39.es/proposal-error-cause/ + // InstallErrorCause(O, options). + // If Type(options) is Object and ? HasProperty(options, "cause") is true + if (Handle options = args.dyncastArg(1)) { + GCScopeMarkerRAII marker{runtime}; + NamedPropertyDescriptor desc; + Handle propObj = + runtime->makeHandle(JSObject::getNamedDescriptorPredefined( + options, runtime, Predefined::cause, desc)); + if (propObj) { + // a. Let cause be ? Get(options, "cause"). + auto causeRes = JSObject::getNamedPropertyValue_RJS( + selfHandle, runtime, std::move(propObj), desc); + if (LLVM_UNLIKELY(causeRes == ExecutionStatus::EXCEPTION)) { + return ExecutionStatus::EXCEPTION; + } + Handle<> cause = runtime->makeHandle(std::move(*causeRes)); + // b. Perform ! CreateNonEnumerableDataPropertyOrThrow(O, "cause", cause). + if (LLVM_UNLIKELY( + JSObject::defineOwnProperty( + selfHandle, + runtime, + Predefined::getSymbolID(Predefined::cause), + DefinePropertyFlags::getNewNonEnumerableFlags(), + cause, + PropOpFlags().plusThrowOnError()) == + ExecutionStatus::EXCEPTION)) { + return ExecutionStatus::EXCEPTION; + } + } + } + return selfHandle.getHermesValue(); } diff --git a/test/hermes/error-cause.js b/test/hermes/error-cause.js new file mode 100644 index 00000000000..0663ad5a871 --- /dev/null +++ b/test/hermes/error-cause.js @@ -0,0 +1,47 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// RUN: %hermes -O %s | %FileCheck --match-full-lines %s +// RUN: %hermes -O -emit-binary -out %t.hbc %s && %hermes %t.hbc | %FileCheck --match-full-lines %s + +print('error cause'); +// CHECK-LABEL: error cause + +try { + try { + throw Error("err1"); + } catch (e1) { + throw Error("err2", { cause: e1 }); + } +} catch (e2) { + print(e2.message); +// CHECK-NEXT: err2 + print(e2.cause.message); +// CHECK-NEXT: err1 +} + +try { + throw Error("err", {get cause() { + print('getter'); + return 'foo'; + }}); +// CHECK-NEXT: getter +} catch (e) { + print(e.message); +// CHECK-NEXT: err + print(e.cause); +// CHECK-NEXT: foo +} + +try { + throw Error("err", {}); +} catch (e) { + print(e.message); +// CHECK-NEXT: err + print(Object.hasOwnProperty(e, 'cause')); +// CHECK-NEXT: false +}