Skip to content

Commit ed2bf35

Browse files
committed
Add a test that synchronous module execution errors are not treated as unhandled promise rejections.
(cherry picked from commit 60ca7d7)
1 parent 3952a50 commit ed2bf35

File tree

1 file changed

+169
-0
lines changed
  • graal-js/src/com.oracle.truffle.js.test/src/com/oracle/truffle/js/test/regress

1 file changed

+169
-0
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.truffle.js.test.regress;
42+
43+
import static com.oracle.truffle.js.runtime.JSContextOptions.UNHANDLED_REJECTIONS_NAME;
44+
import static org.hamcrest.CoreMatchers.containsString;
45+
import static org.hamcrest.CoreMatchers.equalTo;
46+
import static org.hamcrest.CoreMatchers.not;
47+
import static org.hamcrest.MatcherAssert.assertThat;
48+
import static org.junit.Assert.assertFalse;
49+
import static org.junit.Assert.assertTrue;
50+
import static org.junit.Assert.fail;
51+
52+
import java.io.ByteArrayOutputStream;
53+
import java.io.IOException;
54+
import java.util.List;
55+
import java.util.Map;
56+
57+
import org.graalvm.polyglot.Context;
58+
import org.graalvm.polyglot.PolyglotException;
59+
import org.graalvm.polyglot.Source;
60+
import org.graalvm.polyglot.io.IOAccess;
61+
import org.junit.Test;
62+
import org.junit.runner.RunWith;
63+
import org.junit.runners.Parameterized;
64+
import org.junit.runners.Parameterized.Parameter;
65+
import org.junit.runners.Parameterized.Parameters;
66+
67+
import com.oracle.truffle.js.test.JSTest;
68+
import com.oracle.truffle.js.test.polyglot.MockFileSystem;
69+
70+
@RunWith(Parameterized.class)
71+
public class GR61889 {
72+
73+
@Parameters(name = "unhandled-rejections={0}")
74+
public static Iterable<String> data() {
75+
return List.of("none", "throw", "warn");
76+
}
77+
78+
@Parameter(0) public String unhandledRejections;
79+
80+
/**
81+
* Do not treat rejected module evaluation promises as unhandled rejections.
82+
*/
83+
@Test
84+
public void testRejectedModulePromiseIsHandled() throws IOException {
85+
var fs = new MockFileSystem(Map.of(
86+
"eval-throws-error.mjs", """
87+
export function foo() {return 42}
88+
throw new Error("Thrown from within the library");
89+
""",
90+
"eval-throws-non-error.mjs", """
91+
export function foo() {return 42}
92+
throw "Thrown from within the library";
93+
"""));
94+
95+
try (var out = new ByteArrayOutputStream();
96+
Context c = JSTest.newContextBuilder().//
97+
option(UNHANDLED_REJECTIONS_NAME, unhandledRejections).//
98+
allowIO(IOAccess.newBuilder().fileSystem(fs).build()).//
99+
out(out).err(out).build()) {
100+
// Error thrown from another synchronously executed module.
101+
for (String moduleCode : List.of(
102+
"import * as throws from './eval-throws-error.mjs';",
103+
"import * as throws from './eval-throws-non-error.mjs';")) {
104+
try {
105+
c.eval(Source.newBuilder("js", moduleCode, "main.mjs").buildLiteral());
106+
fail("should have thrown");
107+
} catch (PolyglotException e) {
108+
assertFalse(e.getMessage(), e.isSyntaxError());
109+
assertTrue(e.getMessage(), e.isGuestException());
110+
assertThat(e.getMessage(), containsString("Thrown from within the library"));
111+
assertThat(e.getMessage(), not(containsString("Unhandled promise rejection")));
112+
}
113+
}
114+
assertThat("No unhandled rejection warnings", out.toString(), equalTo(""));
115+
}
116+
117+
try (var out = new ByteArrayOutputStream();
118+
Context c = JSTest.newContextBuilder().//
119+
option(UNHANDLED_REJECTIONS_NAME, unhandledRejections).//
120+
allowIO(IOAccess.newBuilder().fileSystem(fs).build()).//
121+
out(out).err(out).build()) {
122+
// Error object thrown from the main module.
123+
for (String moduleCode : List.of("""
124+
export function foo() {return 42}
125+
throw new Error("Thrown from within main module");
126+
""", """
127+
export function foo() {return 42}
128+
throw "Thrown from within main module";
129+
""")) {
130+
try {
131+
c.eval(Source.newBuilder("js", moduleCode, "main.mjs").buildLiteral());
132+
fail("should have thrown");
133+
} catch (PolyglotException e) {
134+
assertFalse(e.getMessage(), e.isSyntaxError());
135+
assertTrue(e.getMessage(), e.isGuestException());
136+
assertThat(e.getMessage(), containsString("Thrown from within main module"));
137+
assertThat(e.getMessage(), not(containsString("Unhandled promise rejection")));
138+
}
139+
assertThat("No unhandled rejection warnings", out.toString(), equalTo(""));
140+
}
141+
}
142+
}
143+
144+
/**
145+
* Do not treat rejected module loading promises as unhandled rejections.
146+
*/
147+
@Test
148+
public void testRejectedLoadRequestedModulesPromiseIsHandled() throws IOException {
149+
var fs = new MockFileSystem(Map.of());
150+
try (var out = new ByteArrayOutputStream();
151+
Context c = JSTest.newContextBuilder().//
152+
option(UNHANDLED_REJECTIONS_NAME, unhandledRejections).//
153+
allowIO(IOAccess.newBuilder().fileSystem(fs).build()).//
154+
out(out).err(out).build()) {
155+
try {
156+
c.eval(Source.newBuilder("js", """
157+
import * as throws from "./does_not_exist.mjs";
158+
""", "./throws-during-load.mjs").buildLiteral());
159+
fail("should have thrown");
160+
} catch (PolyglotException e) {
161+
assertFalse(e.getMessage(), e.isSyntaxError());
162+
assertTrue(e.getMessage(), e.isGuestException());
163+
assertThat(e.getMessage(), containsString("Cannot find module"));
164+
assertThat(e.getMessage(), not(containsString("Unhandled promise rejection")));
165+
}
166+
assertThat(out.toString(), equalTo(""));
167+
}
168+
}
169+
}

0 commit comments

Comments
 (0)