@@ -1209,10 +1209,24 @@ TEST_P(JSITest, JSErrorsCanBeConstructedWithStack) {
12091209}
12101210
12111211TEST_P (JSITest, JSErrorDoesNotInfinitelyRecurse) {
1212- Value globalError = rt.global ().getProperty (rt, " Error" );
1213- rt.global ().setProperty (rt, " Error" , Value::undefined ());
1212+ // JSError(rt, message) creates its value with createError. Use the default
1213+ // implementation, which calls the global Error constructor, so that removing
1214+ // it below makes creating the Error fail even if the runtime has a native
1215+ // createError.
1216+ class RD : public RuntimeDecorator <Runtime, Runtime> {
1217+ public:
1218+ explicit RD (Runtime& rt) : RuntimeDecorator(rt) {}
1219+
1220+ Value createError (const String& msg) override {
1221+ return Runtime::createError (msg);
1222+ }
1223+ };
1224+ RD drt (rt);
1225+
1226+ Value globalError = drt.global ().getProperty (drt, " Error" );
1227+ drt.global ().setProperty (drt, " Error" , Value::undefined ());
12141228 try {
1215- rt .global ().getPropertyAsFunction (rt , " NotAFunction" );
1229+ drt .global ().getPropertyAsFunction (drt , " NotAFunction" );
12161230 FAIL () << " expected exception" ;
12171231 } catch (const JSError& ex) {
12181232 EXPECT_EQ (
@@ -1235,14 +1249,14 @@ TEST_P(JSITest, JSErrorDoesNotInfinitelyRecurse) {
12351249 " function (f) { try { f(); return 'undefined'; }"
12361250 " catch (e) { return typeof e; } }" )
12371251 .call (
1238- rt ,
1252+ drt ,
12391253 Function::createFromHostFunction (
1240- rt , PropNameID::forAscii (rt , " fails" ), 0 , fails))
1241- .getString (rt )
1242- .utf8 (rt ),
1254+ drt , PropNameID::forAscii (drt , " fails" ), 0 , fails))
1255+ .getString (drt )
1256+ .utf8 (drt ),
12431257 " string" );
12441258
1245- rt .global ().setProperty (rt , " Error" , globalError);
1259+ drt .global ().setProperty (drt , " Error" , globalError);
12461260}
12471261
12481262TEST_P (JSITest, JSErrorStackOverflowHandling) {
@@ -1655,6 +1669,155 @@ TEST_P(JSITest, CreateErrorTest) {
16551669 }
16561670}
16571671
1672+ TEST_P (JSITest, JSErrorMessageTest) {
1673+ // JSError(rt, message) creates a new Error instance.
1674+ {
1675+ JSError e (rt, " native error" );
1676+ EXPECT_EQ (e.getMessage (), " native error" );
1677+ Object obj = e.value ().getObject (rt);
1678+ EXPECT_TRUE (
1679+ rt.instanceOf (obj, rt.global ().getPropertyAsFunction (rt, " Error" )));
1680+ EXPECT_EQ (
1681+ obj.getProperty (rt, " message" ).getString (rt).utf8 (rt), " native error" );
1682+ }
1683+
1684+ // JSError(rt, message) creates its value with Runtime::createError, so a
1685+ // replaced global Error constructor is invoked exactly when createError
1686+ // invokes it (e.g. the default jsi::Runtime implementation does, runtimes
1687+ // with a native implementation do not).
1688+ eval (
1689+ " var OriginalError = Error;"
1690+ " var replacedErrorCalls = 0;"
1691+ " Error = function(message) {"
1692+ " ++replacedErrorCalls;"
1693+ " return new OriginalError(message);"
1694+ " };" );
1695+ auto replacedErrorCalls = [&] {
1696+ return rt.global ().getProperty (rt, " replacedErrorCalls" ).getNumber ();
1697+ };
1698+
1699+ rt.createError (String::createFromAscii (rt, " probe" ));
1700+ double callsPerCreateError = replacedErrorCalls ();
1701+
1702+ JSError e (rt, " replaced" );
1703+ EXPECT_EQ (replacedErrorCalls (), 2 * callsPerCreateError);
1704+ EXPECT_EQ (e.getMessage (), " replaced" );
1705+ Object obj = e.value ().getObject (rt);
1706+ EXPECT_TRUE (rt.instanceOf (
1707+ obj, rt.global ().getPropertyAsFunction (rt, " OriginalError" )));
1708+ EXPECT_EQ (obj.getProperty (rt, " message" ).getString (rt).utf8 (rt), " replaced" );
1709+ }
1710+
1711+ TEST_P (JSITest, DecoratorCreateErrorTest) {
1712+ // Counts the createError* calls that reach it, and forwards them to the
1713+ // underlying runtime.
1714+ class CountingRD : public RuntimeDecorator <Runtime, Runtime> {
1715+ public:
1716+ explicit CountingRD (Runtime& rt) : RuntimeDecorator(rt) {}
1717+
1718+ Value createError (const String& msg) override {
1719+ ++count;
1720+ return RuntimeDecorator::createError (msg);
1721+ }
1722+ Value createEvalError (const String& msg) override {
1723+ ++count;
1724+ return RuntimeDecorator::createEvalError (msg);
1725+ }
1726+ Value createRangeError (const String& msg) override {
1727+ ++count;
1728+ return RuntimeDecorator::createRangeError (msg);
1729+ }
1730+ Value createReferenceError (const String& msg) override {
1731+ ++count;
1732+ return RuntimeDecorator::createReferenceError (msg);
1733+ }
1734+ Value createSyntaxError (const String& msg) override {
1735+ ++count;
1736+ return RuntimeDecorator::createSyntaxError (msg);
1737+ }
1738+ Value createTypeError (const String& msg) override {
1739+ ++count;
1740+ return RuntimeDecorator::createTypeError (msg);
1741+ }
1742+ Value createURIError (const String& msg) override {
1743+ ++count;
1744+ return RuntimeDecorator::createURIError (msg);
1745+ }
1746+
1747+ int count = 0 ;
1748+ };
1749+
1750+ class RD : public RuntimeDecorator <Runtime, Runtime> {
1751+ public:
1752+ explicit RD (Runtime& rt) : RuntimeDecorator(rt) {}
1753+ };
1754+
1755+ struct Count {
1756+ void before () {
1757+ ++count;
1758+ }
1759+
1760+ int count = 0 ;
1761+ };
1762+
1763+ class CountRuntime : public WithRuntimeDecorator <Count> {
1764+ public:
1765+ explicit CountRuntime (Runtime& rt)
1766+ : WithRuntimeDecorator<Count>(rt, count_) {}
1767+
1768+ Count count_;
1769+ };
1770+
1771+ CountingRD counting (rt);
1772+ RD rd (counting);
1773+ CountRuntime crt (counting);
1774+
1775+ struct {
1776+ Value (Runtime::*create)(const String&);
1777+ const char * ctor;
1778+ } cases[] = {
1779+ {&Runtime::createError, " Error" },
1780+ {&Runtime::createEvalError, " EvalError" },
1781+ {&Runtime::createRangeError, " RangeError" },
1782+ {&Runtime::createReferenceError, " ReferenceError" },
1783+ {&Runtime::createSyntaxError, " SyntaxError" },
1784+ {&Runtime::createTypeError, " TypeError" },
1785+ {&Runtime::createURIError, " URIError" },
1786+ };
1787+
1788+ // Both decorators must forward createError* to the decorated runtime rather
1789+ // than fall back to the default implementation calling the global ctor.
1790+ for (const auto & c : cases) {
1791+ for (Runtime* drt :
1792+ {static_cast <Runtime*>(&rd), static_cast <Runtime*>(&crt)}) {
1793+ String msg = String::createFromAscii (*drt, " decorated" );
1794+ int countBefore = counting.count ;
1795+ int aroundBefore = crt.count_ .count ;
1796+ Value error = (drt->*c.create )(msg);
1797+ EXPECT_EQ (counting.count , countBefore + 1 ) << c.ctor ;
1798+ if (drt == &crt) {
1799+ EXPECT_EQ (crt.count_ .count , aroundBefore + 1 ) << c.ctor ;
1800+ }
1801+
1802+ Object obj = error.getObject (*drt);
1803+ EXPECT_TRUE (drt->instanceOf (
1804+ obj, drt->global ().getPropertyAsFunction (*drt, c.ctor )))
1805+ << c.ctor ;
1806+ EXPECT_EQ (
1807+ obj.getProperty (*drt, " message" ).getString (*drt).utf8 (*drt),
1808+ " decorated" )
1809+ << c.ctor ;
1810+ }
1811+ }
1812+
1813+ // JSError(rt, message) on a decorated runtime reaches the decorated
1814+ // runtime's createError.
1815+ int countBefore = counting.count ;
1816+ JSError e (rd, " decorated JSError" );
1817+ EXPECT_EQ (counting.count , countBefore + 1 );
1818+ EXPECT_EQ (e.getMessage (), " decorated JSError" );
1819+ }
1820+
16581821TEST_P (JSITest, MicrotasksTest) {
16591822 try {
16601823 rt.global ().setProperty (rt, " globalValue" , String::createFromAscii (rt, " " ));
0 commit comments