mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 07:15:46 +00:00
Merge mozilla-central to autoland. a=merge
--HG-- extra : rebase_source : d2d1fef29703d48d14b7ceac0ffa9ee0e5a0a007
This commit is contained in:
commit
8b0d7e9861
@ -21,11 +21,6 @@ function handleThreadState(toolbox, event, packet) {
|
||||
// threadClient now.
|
||||
toolbox.target.emit("thread-" + event);
|
||||
|
||||
const replayButton = toolbox.doc.getElementById("command-button-stop-replay");
|
||||
if (replayButton) {
|
||||
replayButton.classList.toggle("paused", event === "paused");
|
||||
}
|
||||
|
||||
if (event === "paused") {
|
||||
toolbox.highlightTool("jsdebugger");
|
||||
|
||||
@ -59,6 +54,13 @@ function attachThread(toolbox) {
|
||||
threadClient.addListener("paused", handleThreadState.bind(null, toolbox));
|
||||
threadClient.addListener("resumed", handleThreadState.bind(null, toolbox));
|
||||
|
||||
threadClient.addListener("progress", (_, {recording}) => {
|
||||
const replayButton = toolbox.doc.getElementById("command-button-stop-replay");
|
||||
if (replayButton) {
|
||||
replayButton.classList.toggle("recording", recording);
|
||||
}
|
||||
});
|
||||
|
||||
if (!threadClient.paused) {
|
||||
reject(new Error("Thread in wrong state when starting up, should be paused"));
|
||||
}
|
||||
|
@ -357,11 +357,11 @@
|
||||
}
|
||||
|
||||
#command-button-stop-replay::before {
|
||||
fill: var(--red-60);
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
#command-button-stop-replay.paused::before{
|
||||
fill: currentColor;
|
||||
#command-button-stop-replay.recording::before{
|
||||
fill: var(--red-60);
|
||||
}
|
||||
|
||||
#command-button-scratchpad::before {
|
||||
@ -516,6 +516,10 @@
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.webreplay-player .command-button.active:hover {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.webreplay-player .play-button {
|
||||
mask-image: var(--play-image);
|
||||
margin-right: 5px;
|
||||
|
@ -115,7 +115,7 @@ a {
|
||||
opacity: 0.6;
|
||||
width: 100vw;
|
||||
height: 1px;
|
||||
bottom: 0px;
|
||||
top: 0px;
|
||||
left: -3px;
|
||||
display: block;
|
||||
content: "";
|
||||
|
@ -26,6 +26,25 @@ const {
|
||||
getInitialMessageCountForViewport,
|
||||
} = require("devtools/client/webconsole/utils/messages.js");
|
||||
|
||||
// Finds the message that comes right after the current paused execution point.
|
||||
// NOTE: visibleMessages are not guaranteed to be ordered.
|
||||
function getPausedMessage(visibleMessages, messages, executionPoint) {
|
||||
if (!executionPoint || !visibleMessages) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let pausedMessage = messages.get(visibleMessages[0]);
|
||||
for (const messageId of visibleMessages) {
|
||||
const message = messages.get(messageId);
|
||||
if (executionPoint.progress >= message.executionPoint.progress &&
|
||||
message.executionPoint.progress > pausedMessage.executionPoint.progress) {
|
||||
pausedMessage = message;
|
||||
}
|
||||
}
|
||||
|
||||
return pausedMessage;
|
||||
}
|
||||
|
||||
class ConsoleOutput extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
@ -145,6 +164,9 @@ class ConsoleOutput extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
const pausedMessage = getPausedMessage(
|
||||
visibleMessages, messages, pausedExecutionPoint);
|
||||
|
||||
const messageNodes = visibleMessages.map((messageId) => MessageContainer({
|
||||
dispatch,
|
||||
key: messageId,
|
||||
@ -158,6 +180,7 @@ class ConsoleOutput extends Component {
|
||||
networkMessageActiveTabId,
|
||||
pausedExecutionPoint,
|
||||
getMessage: () => messages.get(messageId),
|
||||
isPaused: pausedMessage && pausedMessage.id == messageId,
|
||||
}));
|
||||
|
||||
return (
|
||||
|
@ -24,13 +24,6 @@ const componentMap = new Map([
|
||||
["PageError", require("./message-types/PageError")],
|
||||
]);
|
||||
|
||||
function isPaused({ getMessage, pausedExecutionPoint }) {
|
||||
const message = getMessage();
|
||||
return pausedExecutionPoint
|
||||
&& message.executionPoint
|
||||
&& pausedExecutionPoint.progress === message.executionPoint.progress;
|
||||
}
|
||||
|
||||
class MessageContainer extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
@ -42,6 +35,7 @@ class MessageContainer extends Component {
|
||||
repeat: PropTypes.number,
|
||||
networkMessageUpdate: PropTypes.object,
|
||||
getMessage: PropTypes.func.isRequired,
|
||||
isPaused: PropTypes.bool.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
@ -59,7 +53,7 @@ class MessageContainer extends Component {
|
||||
this.props.timestampsVisible !== nextProps.timestampsVisible;
|
||||
const networkMessageUpdateChanged =
|
||||
this.props.networkMessageUpdate !== nextProps.networkMessageUpdate;
|
||||
const pausedChanged = isPaused(this.props) !== isPaused(nextProps);
|
||||
const pausedChanged = this.props.isPaused !== nextProps.isPaused;
|
||||
|
||||
return repeatChanged
|
||||
|| openChanged
|
||||
@ -73,9 +67,7 @@ class MessageContainer extends Component {
|
||||
const message = this.props.getMessage();
|
||||
|
||||
const MessageComponent = getMessageComponent(message);
|
||||
return MessageComponent(Object.assign({message}, this.props, {
|
||||
isPaused: isPaused(this.props),
|
||||
}));
|
||||
return MessageComponent(Object.assign({message}, this.props));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,8 @@ WebConsoleOutputWrapper.prototype = {
|
||||
|
||||
if (this.toolbox) {
|
||||
this.toolbox.threadClient.addListener("paused", this.dispatchPaused.bind(this));
|
||||
this.toolbox.threadClient.addListener("resumed", this.dispatchResumed.bind(this));
|
||||
this.toolbox.threadClient.addListener(
|
||||
"progress", this.dispatchProgress.bind(this));
|
||||
|
||||
Object.assign(serviceContainer, {
|
||||
onViewSourceInDebugger: frame => {
|
||||
@ -365,8 +366,10 @@ WebConsoleOutputWrapper.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
dispatchResumed: function(_, packet) {
|
||||
store.dispatch(actions.setPauseExecutionPoint(null));
|
||||
dispatchProgress: function(_, packet) {
|
||||
const {executionPoint, recording} = packet;
|
||||
const point = recording ? null : executionPoint;
|
||||
store.dispatch(actions.setPauseExecutionPoint(point));
|
||||
},
|
||||
|
||||
dispatchMessageUpdate: function(message, res) {
|
||||
|
@ -21,6 +21,7 @@ class WebReplayPlayer extends Component {
|
||||
executionPoint: null,
|
||||
recordingEndpoint: null,
|
||||
seeking: false,
|
||||
recording: true,
|
||||
messages: [],
|
||||
};
|
||||
}
|
||||
@ -28,6 +29,7 @@ class WebReplayPlayer extends Component {
|
||||
componentDidMount() {
|
||||
this.threadClient.addListener("paused", this.onPaused.bind(this));
|
||||
this.threadClient.addListener("resumed", this.onResumed.bind(this));
|
||||
this.threadClient.addListener("progress", this.onProgress.bind(this));
|
||||
this.activeConsole._client.addListener(
|
||||
"consoleAPICall",
|
||||
this.onMessage.bind(this)
|
||||
@ -42,20 +44,34 @@ class WebReplayPlayer extends Component {
|
||||
return this.props.toolbox.target.activeConsole;
|
||||
}
|
||||
|
||||
isRecording() {
|
||||
return this.state.recording;
|
||||
}
|
||||
|
||||
isReplaying() {
|
||||
const {recording} = this.state;
|
||||
return !this.isPaused() && !recording;
|
||||
}
|
||||
|
||||
isPaused() {
|
||||
const { executionPoint, seeking } = this.state;
|
||||
return !!executionPoint || !!seeking;
|
||||
const { paused } = this.state;
|
||||
return paused;
|
||||
}
|
||||
|
||||
onPaused(_, packet) {
|
||||
if (packet && packet.recordingEndpoint) {
|
||||
const { executionPoint, recordingEndpoint } = packet;
|
||||
this.setState({ executionPoint, recordingEndpoint, seeking: false });
|
||||
this.setState({ executionPoint, recordingEndpoint, paused: true });
|
||||
}
|
||||
}
|
||||
|
||||
onResumed(_, packet) {
|
||||
this.setState({ executionPoint: null });
|
||||
this.setState({ paused: false });
|
||||
}
|
||||
|
||||
onProgress(_, packet) {
|
||||
const { recording, executionPoint } = packet;
|
||||
this.setState({ recording, executionPoint });
|
||||
}
|
||||
|
||||
onMessage(_, packet) {
|
||||
@ -68,25 +84,36 @@ class WebReplayPlayer extends Component {
|
||||
}
|
||||
|
||||
// set seeking to the current execution point to avoid a progress bar jump
|
||||
this.setState({ seeking: this.state.executionPoint });
|
||||
return this.threadClient.timeWarp(executionPoint);
|
||||
}
|
||||
|
||||
next(ev) {
|
||||
if (!this.isPaused()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!ev.metaKey) {
|
||||
return this.threadClient.resume();
|
||||
}
|
||||
|
||||
const { messages, executionPoint } = this.state;
|
||||
const seekPoint = messages
|
||||
const { messages, executionPoint, recordingEndpoint } = this.state;
|
||||
let seekPoint = messages
|
||||
.map(m => m.executionPoint)
|
||||
.filter(point => point.progress > executionPoint.progress)
|
||||
.slice(0)[0];
|
||||
|
||||
if (!seekPoint) {
|
||||
seekPoint = recordingEndpoint;
|
||||
}
|
||||
|
||||
return this.seek(seekPoint);
|
||||
}
|
||||
|
||||
previous(ev) {
|
||||
if (!this.isPaused()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!ev.metaKey) {
|
||||
return this.threadClient.rewind();
|
||||
}
|
||||
@ -102,25 +129,7 @@ class WebReplayPlayer extends Component {
|
||||
}
|
||||
|
||||
renderCommands() {
|
||||
if (this.isPaused()) {
|
||||
return [
|
||||
div(
|
||||
{ className: "command-button" },
|
||||
div({
|
||||
className: "rewind-button btn",
|
||||
onClick: ev => this.previous(ev),
|
||||
})
|
||||
),
|
||||
div(
|
||||
{ className: "command-button" },
|
||||
div({
|
||||
className: "play-button btn",
|
||||
onClick: ev => this.next(ev),
|
||||
})
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
if (this.isRecording()) {
|
||||
return [
|
||||
div(
|
||||
{ className: "command-button" },
|
||||
@ -132,9 +141,33 @@ class WebReplayPlayer extends Component {
|
||||
];
|
||||
}
|
||||
|
||||
const isActiveClass = !this.isPaused() ? "active" : "";
|
||||
|
||||
return [
|
||||
div(
|
||||
{ className: `command-button ${isActiveClass}` },
|
||||
div({
|
||||
className: "rewind-button btn",
|
||||
onClick: ev => this.previous(ev),
|
||||
})
|
||||
),
|
||||
div(
|
||||
{ className: `command-button ${isActiveClass}` },
|
||||
div({
|
||||
className: "play-button btn",
|
||||
onClick: ev => this.next(ev),
|
||||
})
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
renderMessages() {
|
||||
const messages = this.state.messages;
|
||||
|
||||
if (this.isRecording()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return messages.map((message, index) =>
|
||||
dom.div({
|
||||
className: "message",
|
||||
@ -169,9 +202,7 @@ class WebReplayPlayer extends Component {
|
||||
div({
|
||||
className: "progress",
|
||||
style: {
|
||||
width: `${this.getPercent(
|
||||
this.state.executionPoint || this.state.seeking
|
||||
)}%`,
|
||||
width: `${this.getPercent(this.state.executionPoint)}%`,
|
||||
},
|
||||
}),
|
||||
...this.renderMessages()
|
||||
|
@ -28,6 +28,7 @@ loader.lazyRequireGetter(this, "PauseScopedObjectActor", "devtools/server/actors
|
||||
loader.lazyRequireGetter(this, "EventLoopStack", "devtools/server/actors/utils/event-loop", true);
|
||||
loader.lazyRequireGetter(this, "FrameActor", "devtools/server/actors/frame", true);
|
||||
loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
|
||||
loader.lazyRequireGetter(this, "throttle", "devtools/shared/throttle", true);
|
||||
|
||||
/**
|
||||
* JSD2 actors.
|
||||
@ -113,6 +114,8 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
this._dbg.onNewScript = this.onNewScript;
|
||||
if (this._dbg.replaying) {
|
||||
this._dbg.replayingOnForcedPause = this.replayingOnForcedPause.bind(this);
|
||||
this._dbg.replayingOnPositionChange =
|
||||
throttle(this.replayingOnPositionChange.bind(this), 100);
|
||||
}
|
||||
// Keep the debugger disabled until a client attaches.
|
||||
this._dbg.enabled = this._state != "detached";
|
||||
@ -1778,6 +1781,17 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
|
||||
return { skip };
|
||||
},
|
||||
|
||||
/*
|
||||
* A function that the engine calls when a recording/replaying process has
|
||||
* changed its position: a checkpoint was reached or a switch between a
|
||||
* recording and replaying child process occurred.
|
||||
*/
|
||||
replayingOnPositionChange: function() {
|
||||
const recording = this.dbg.replayIsRecording();
|
||||
const executionPoint = this.dbg.replayCurrentExecutionPoint();
|
||||
this.conn.send({ type: "progress", from: this.actorID, recording, executionPoint });
|
||||
},
|
||||
|
||||
/**
|
||||
* A function that the engine calls when replay has hit a point where it will
|
||||
* pause, even if no breakpoint has been set. Such points include hitting the
|
||||
|
@ -754,7 +754,7 @@ ThreadClient.prototype = {
|
||||
actors: arg(0),
|
||||
}),
|
||||
|
||||
events: ["newSource"],
|
||||
events: ["newSource", "progress"],
|
||||
};
|
||||
|
||||
eventSource(ThreadClient.prototype);
|
||||
|
@ -2702,7 +2702,9 @@ nsJSContext::EnsureStatics()
|
||||
JS::SetAsmJSCacheOps(jsapi.cx(), &asmJSCacheOps);
|
||||
|
||||
JS::InitDispatchToEventLoop(jsapi.cx(), DispatchToEventLoop, nullptr);
|
||||
JS::InitConsumeStreamCallback(jsapi.cx(), ConsumeStream);
|
||||
JS::InitConsumeStreamCallback(jsapi.cx(),
|
||||
ConsumeStream,
|
||||
FetchUtil::ReportJSStreamError);
|
||||
|
||||
// Set these global xpconnect options...
|
||||
Preferences::RegisterCallbackAndCall(SetMemoryPrefChangedCallbackMB,
|
||||
|
@ -484,16 +484,16 @@ public:
|
||||
}
|
||||
|
||||
if (rv == NS_BASE_STREAM_CLOSED) {
|
||||
mConsumer->streamClosed(JS::StreamConsumer::EndOfFile);
|
||||
mConsumer->streamEnd();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
mConsumer->streamClosed(JS::StreamConsumer::Error);
|
||||
mConsumer->streamError(size_t(rv));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Check mConsumerAborted before NS_FAILED to avoid calling streamClosed()
|
||||
// Check mConsumerAborted before NS_FAILED to avoid calling streamError()
|
||||
// if consumeChunk() returned false per JS API contract.
|
||||
uint32_t written = 0;
|
||||
rv = aStream->ReadSegments(WriteSegment, this, available, &written);
|
||||
@ -501,13 +501,13 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mConsumer->streamClosed(JS::StreamConsumer::Error);
|
||||
mConsumer->streamError(size_t(rv));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
rv = aStream->AsyncWait(this, 0, 0, nullptr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mConsumer->streamClosed(JS::StreamConsumer::Error);
|
||||
mConsumer->streamError(size_t(rv));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -595,7 +595,7 @@ FetchUtil::StreamResponseToJS(JSContext* aCx,
|
||||
nsCOMPtr<nsIInputStream> body;
|
||||
ir->GetUnfilteredBody(getter_AddRefs(body));
|
||||
if (!body) {
|
||||
aConsumer->streamClosed(JS::StreamConsumer::EndOfFile);
|
||||
aConsumer->streamEnd();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -614,5 +614,21 @@ FetchUtil::StreamResponseToJS(JSContext* aCx,
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void
|
||||
FetchUtil::ReportJSStreamError(JSContext* aCx, size_t aErrorCode)
|
||||
{
|
||||
// For now, convert *all* errors into AbortError.
|
||||
|
||||
RefPtr<DOMException> e = DOMException::Create(NS_ERROR_DOM_ABORT_ERR);
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
if (!GetOrCreateDOMReflector(aCx, e, &value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
JS_SetPendingException(aCx, value);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -67,6 +67,16 @@ public:
|
||||
JS::MimeType aMimeType,
|
||||
JS::StreamConsumer* aConsumer,
|
||||
WorkerPrivate* aMaybeWorker);
|
||||
|
||||
/**
|
||||
* Called by JS to report (i.e., throw) an error that was passed to the
|
||||
* JS::StreamConsumer::streamError() method on a random stream thread.
|
||||
* This method is passed by function pointer to the JS engine hence the
|
||||
* untyped 'size_t' instead of Gecko 'nsresult'.
|
||||
*/
|
||||
static void
|
||||
ReportJSStreamError(JSContext* aCx,
|
||||
size_t aErrorCode);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -896,7 +896,9 @@ InitJSContextForWorker(WorkerPrivate* aWorkerPrivate, JSContext* aWorkerCx)
|
||||
// store a raw pointer as the callback's closure argument on the JSRuntime.
|
||||
JS::InitDispatchToEventLoop(aWorkerCx, DispatchToEventLoop, (void*)aWorkerPrivate);
|
||||
|
||||
JS::InitConsumeStreamCallback(aWorkerCx, ConsumeStream);
|
||||
JS::InitConsumeStreamCallback(aWorkerCx,
|
||||
ConsumeStream,
|
||||
FetchUtil::ReportJSStreamError);
|
||||
|
||||
if (!JS::InitSelfHostedCode(aWorkerCx)) {
|
||||
NS_WARNING("Could not init self-hosted code!");
|
||||
|
6
js/src/jit-test/tests/wasm/regress/bug1507314.js
Normal file
6
js/src/jit-test/tests/wasm/regress/bug1507314.js
Normal file
@ -0,0 +1,6 @@
|
||||
// |jit-test| --no-sse4
|
||||
if (!wasmCachingIsSupported())
|
||||
quit(0);
|
||||
|
||||
var m = wasmCompileInSeparateProcess(wasmTextToBinary('(module (func (export "run") (result i32) (i32.const 42)))'));
|
||||
assertEq(new WebAssembly.Instance(m).exports.run(), 42);
|
@ -417,7 +417,6 @@ MSG_DEF(JSMSG_WASM_BAD_TABLE_VALUE, 0, JSEXN_TYPEERR, "can only assign We
|
||||
MSG_DEF(JSMSG_WASM_BAD_I64_TYPE, 0, JSEXN_TYPEERR, "cannot pass i64 to or from JS")
|
||||
MSG_DEF(JSMSG_WASM_BAD_GLOBAL_TYPE, 0, JSEXN_TYPEERR, "bad type for a WebAssembly.Global")
|
||||
MSG_DEF(JSMSG_WASM_NO_TRANSFER, 0, JSEXN_TYPEERR, "cannot transfer WebAssembly/asm.js ArrayBuffer")
|
||||
MSG_DEF(JSMSG_WASM_STREAM_ERROR, 0, JSEXN_TYPEERR, "stream error during WebAssembly compilation")
|
||||
MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {0}")
|
||||
MSG_DEF(JSMSG_WASM_MISSING_MAXIMUM, 0, JSEXN_TYPEERR, "'shared' is true but maximum is not specified")
|
||||
MSG_DEF(JSMSG_WASM_GLOBAL_IMMUTABLE, 0, JSEXN_TYPEERR, "can't set value of immutable global")
|
||||
@ -669,6 +668,7 @@ MSG_DEF(JSMSG_READABLESTREAM_METHOD_NOT_IMPLEMENTED, 1, JSEXN_TYPEERR, "Read
|
||||
|
||||
// Other Stream-related
|
||||
MSG_DEF(JSMSG_STREAM_INVALID_HIGHWATERMARK, 0, JSEXN_RANGEERR, "'highWaterMark' must be a non-negative, non-NaN number.")
|
||||
MSG_DEF(JSMSG_STREAM_CONSUME_ERROR, 0, JSEXN_TYPEERR, "error consuming stream body")
|
||||
|
||||
// Response-related
|
||||
MSG_DEF(JSMSG_ERROR_CONSUMING_RESPONSE, 0, JSEXN_TYPEERR, "there was an error consuming the Response")
|
||||
|
@ -4659,9 +4659,11 @@ JS::GetOptimizedEncodingBuildId(JS::BuildIdCharVector* buildId)
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS::InitConsumeStreamCallback(JSContext* cx, ConsumeStreamCallback callback)
|
||||
JS::InitConsumeStreamCallback(JSContext* cx, ConsumeStreamCallback consume,
|
||||
ReportStreamErrorCallback report)
|
||||
{
|
||||
cx->runtime()->consumeStreamCallback = callback;
|
||||
cx->runtime()->consumeStreamCallback = consume;
|
||||
cx->runtime()->reportStreamErrorCallback = report;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
|
@ -3587,13 +3587,14 @@ typedef js::Vector<char, 0, js::SystemAllocPolicy> BuildIdCharVector;
|
||||
* appropriate error on 'cx'. On success, the embedding must call
|
||||
* consumer->consumeChunk() repeatedly on any thread until exactly one of:
|
||||
* - consumeChunk() returns false
|
||||
* - the embedding calls consumer->streamClosed()
|
||||
* - the embedding calls consumer->streamEnd()
|
||||
* - the embedding calls consumer->streamError()
|
||||
* before JS_DestroyContext(cx) or JS::ShutdownAsyncTasks(cx) is called.
|
||||
*
|
||||
* Note: consumeChunk() and streamClosed() may be called synchronously by
|
||||
* ConsumeStreamCallback.
|
||||
* Note: consumeChunk(), streamEnd() and streamError() may be called
|
||||
* synchronously by ConsumeStreamCallback.
|
||||
*
|
||||
* When streamClosed() is called, the embedding may optionally pass an
|
||||
* When streamEnd() is called, the embedding may optionally pass an
|
||||
* OptimizedEncodingListener*, indicating that there is a cache entry associated
|
||||
* with this stream that can store an optimized encoding of the bytes that were
|
||||
* just streamed at some point in the future by having SpiderMonkey call
|
||||
@ -3601,7 +3602,7 @@ typedef js::Vector<char, 0, js::SystemAllocPolicy> BuildIdCharVector;
|
||||
* will hold an outstanding refcount to keep the listener alive.
|
||||
*
|
||||
* After storeOptimizedEncoding() is called, on cache hit, the embedding
|
||||
* may call consumeOptimizedEncoding() instead of consumeChunk()/streamClosed().
|
||||
* may call consumeOptimizedEncoding() instead of consumeChunk()/streamEnd().
|
||||
* The embedding must ensure that the GetOptimizedEncodingBuildId() at the time
|
||||
* when an optimized encoding is created is the same as when it is later
|
||||
* consumed.
|
||||
@ -3639,19 +3640,22 @@ class JS_PUBLIC_API(StreamConsumer)
|
||||
// this StreamConsumer.
|
||||
virtual bool consumeChunk(const uint8_t* begin, size_t length) = 0;
|
||||
|
||||
// Called by the embedding when the stream is closed according to the
|
||||
// contract described above.
|
||||
enum CloseReason { EndOfFile, Error };
|
||||
virtual void streamClosed(CloseReason reason,
|
||||
OptimizedEncodingListener* listener = nullptr) = 0;
|
||||
// Called by the embedding when the stream reaches end-of-file, passing the
|
||||
// listener described above.
|
||||
virtual void streamEnd(OptimizedEncodingListener* listener = nullptr) = 0;
|
||||
|
||||
// Called by the embedding *instead of* consumeChunk()/streamClosed() if an
|
||||
// Called by the embedding when there is an error during streaming. The
|
||||
// given error code should be passed to the ReportStreamErrorCallback on the
|
||||
// main thread to produce the semantically-correct rejection value.
|
||||
virtual void streamError(size_t errorCode) = 0;
|
||||
|
||||
// Called by the embedding *instead of* consumeChunk()/streamEnd() if an
|
||||
// optimized encoding is available from a previous streaming of the same
|
||||
// contents with the same optimized build id.
|
||||
virtual void consumeOptimizedEncoding(const uint8_t* begin, size_t length) = 0;
|
||||
|
||||
// Provides optional stream attributes such as base or source mapping URLs.
|
||||
// Necessarily called before consumeChunk(), streamClosed() or
|
||||
// Necessarily called before consumeChunk(), streamEnd(), streamError() or
|
||||
// consumeOptimizedEncoding(). The caller retains ownership of the strings.
|
||||
virtual void noteResponseURLs(const char* maybeUrl, const char* maybeSourceMapUrl) = 0;
|
||||
};
|
||||
@ -3662,8 +3666,13 @@ typedef bool
|
||||
(*ConsumeStreamCallback)(JSContext* cx, JS::HandleObject obj, MimeType mimeType,
|
||||
StreamConsumer* consumer);
|
||||
|
||||
typedef void
|
||||
(*ReportStreamErrorCallback)(JSContext* cx, size_t errorCode);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
InitConsumeStreamCallback(JSContext* cx, ConsumeStreamCallback callback);
|
||||
InitConsumeStreamCallback(JSContext* cx,
|
||||
ConsumeStreamCallback consume,
|
||||
ReportStreamErrorCallback report);
|
||||
|
||||
/**
|
||||
* When a JSRuntime is destroyed it implicitly cancels all async tasks in
|
||||
|
@ -6019,6 +6019,8 @@ class AutoPipe
|
||||
}
|
||||
};
|
||||
|
||||
static const char sWasmCompileAndSerializeFlag[] = "--wasm-compile-and-serialize";
|
||||
|
||||
static bool
|
||||
CompileAndSerializeInSeparateProcess(JSContext* cx, const uint8_t* bytecode, size_t bytecodeLength,
|
||||
wasm::Bytes* serialized)
|
||||
@ -6035,8 +6037,19 @@ CompileAndSerializeInSeparateProcess(JSContext* cx, const uint8_t* bytecode, siz
|
||||
return false;
|
||||
}
|
||||
|
||||
UniqueChars argv1 = DuplicateString("--wasm-compile-and-serialize");
|
||||
if (!argv1 || !argv.append(std::move(argv1))) {
|
||||
// Propagate shell flags first, since they must precede the non-option
|
||||
// file-descriptor args (passed on Windows, below).
|
||||
for (unsigned i = 0; i < sPropagatedFlags.length(); i++) {
|
||||
UniqueChars flags = DuplicateString(cx, sPropagatedFlags[i]);
|
||||
if (!flags || !argv.append(std::move(flags))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
UniqueChars arg;
|
||||
|
||||
arg = DuplicateString(sWasmCompileAndSerializeFlag);
|
||||
if (!arg || !argv.append(std::move(arg))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -6047,34 +6060,28 @@ CompileAndSerializeInSeparateProcess(JSContext* cx, const uint8_t* bytecode, siz
|
||||
// has a matching #ifdef XP_WIN to parse them out. Communicate both ends of
|
||||
// both pipes so the child process can closed the unused ends.
|
||||
|
||||
UniqueChars argv2 = JS_smprintf("%d", stdIn.reader());
|
||||
if (!argv2 || !argv.append(std::move(argv2))) {
|
||||
arg = JS_smprintf("%d", stdIn.reader());
|
||||
if (!arg || !argv.append(std::move(arg))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UniqueChars argv3 = JS_smprintf("%d", stdIn.writer());
|
||||
if (!argv3 || !argv.append(std::move(argv3))) {
|
||||
arg = JS_smprintf("%d", stdIn.writer());
|
||||
if (!arg || !argv.append(std::move(arg))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UniqueChars argv4 = JS_smprintf("%d", stdOut.reader());
|
||||
if (!argv4 || !argv.append(std::move(argv4))) {
|
||||
arg = JS_smprintf("%d", stdOut.reader());
|
||||
if (!arg || !argv.append(std::move(arg))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UniqueChars argv5 = JS_smprintf("%d", stdOut.writer());
|
||||
if (!argv5 || !argv.append(std::move(argv5))) {
|
||||
arg = JS_smprintf("%d", stdOut.writer());
|
||||
if (!arg || !argv.append(std::move(arg))) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (unsigned i = 0; i < sPropagatedFlags.length(); i++) {
|
||||
UniqueChars flags = DuplicateString(cx, sPropagatedFlags[i]);
|
||||
if (!flags || !argv.append(std::move(flags))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Required by both _spawnv and exec.
|
||||
if (!argv.append(nullptr)) {
|
||||
return false;
|
||||
}
|
||||
@ -6158,12 +6165,26 @@ WasmCompileAndSerialize(JSContext* cx)
|
||||
// See CompileAndSerializeInSeparateProcess for why we've had to smuggle
|
||||
// these fd values through argv. Closing the writing ends is necessary for
|
||||
// the reading ends to hit EOF.
|
||||
MOZ_RELEASE_ASSERT(sArgc >= 6);
|
||||
MOZ_ASSERT(!strcmp(sArgv[1], "--wasm-compile-and-serialize"));
|
||||
int stdIn = atoi(sArgv[2]); // stdIn.reader()
|
||||
close(atoi(sArgv[3])); // stdIn.writer()
|
||||
close(atoi(sArgv[4])); // stdOut.reader()
|
||||
int stdOut = atoi(sArgv[5]); // stdOut.writer()
|
||||
int flagIndex = 0;
|
||||
for (; flagIndex < sArgc; flagIndex++) {
|
||||
if (!strcmp(sArgv[flagIndex], sWasmCompileAndSerializeFlag)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
MOZ_RELEASE_ASSERT(flagIndex < sArgc);
|
||||
|
||||
int fdsIndex = flagIndex + 1;
|
||||
MOZ_RELEASE_ASSERT(fdsIndex + 4 == sArgc);
|
||||
|
||||
int stdInReader = atoi(sArgv[fdsIndex + 0]);
|
||||
int stdInWriter = atoi(sArgv[fdsIndex + 1]);
|
||||
int stdOutReader = atoi(sArgv[fdsIndex + 2]);
|
||||
int stdOutWriter = atoi(sArgv[fdsIndex + 3]);
|
||||
|
||||
int stdIn = stdInReader;
|
||||
close(stdInWriter);
|
||||
close(stdOutReader);
|
||||
int stdOut = stdOutWriter;
|
||||
#else
|
||||
int stdIn = STDIN_FILENO;
|
||||
int stdOut = STDOUT_FILENO;
|
||||
@ -7389,7 +7410,7 @@ BufferStreamMain(BufferStreamJob* job)
|
||||
byteOffset = 0;
|
||||
while (true) {
|
||||
if (byteOffset == byteLength) {
|
||||
job->consumer->streamClosed(JS::StreamConsumer::EndOfFile, listener);
|
||||
job->consumer->streamEnd(listener);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -7404,7 +7425,7 @@ BufferStreamMain(BufferStreamJob* job)
|
||||
}
|
||||
|
||||
if (shutdown) {
|
||||
job->consumer->streamClosed(JS::StreamConsumer::Error);
|
||||
job->consumer->streamError(JSMSG_STREAM_CONSUME_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -7525,6 +7546,13 @@ ConsumeBufferSource(JSContext* cx, JS::HandleObject obj, JS::MimeType, JS::Strea
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
ReportStreamError(JSContext* cx, size_t errorNumber)
|
||||
{
|
||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
SetBufferStreamParams(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
@ -11348,7 +11376,7 @@ main(int argc, char** argv, char** envp)
|
||||
ShutdownBufferStreams();
|
||||
js_delete(bufferStreamState);
|
||||
});
|
||||
JS::InitConsumeStreamCallback(cx, ConsumeBufferSource);
|
||||
JS::InitConsumeStreamCallback(cx, ConsumeBufferSource, ReportStreamError);
|
||||
|
||||
JS_SetNativeStackQuota(cx, gMaxStackSize);
|
||||
|
||||
|
@ -101,6 +101,7 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
|
||||
profilerSampleBufferRangeStart_(0),
|
||||
telemetryCallback(nullptr),
|
||||
consumeStreamCallback(nullptr),
|
||||
reportStreamErrorCallback(nullptr),
|
||||
readableStreamDataRequestCallback(nullptr),
|
||||
readableStreamWriteIntoReadRequestCallback(nullptr),
|
||||
readableStreamCancelCallback(nullptr),
|
||||
|
@ -339,6 +339,7 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
|
||||
public:
|
||||
js::UnprotectedData<js::OffThreadPromiseRuntimeState> offThreadPromiseState;
|
||||
js::UnprotectedData<JS::ConsumeStreamCallback> consumeStreamCallback;
|
||||
js::UnprotectedData<JS::ReportStreamErrorCallback> reportStreamErrorCallback;
|
||||
|
||||
js::GlobalObject* getIncumbentGlobal(JSContext* cx);
|
||||
bool enqueuePromiseJob(JSContext* cx, js::HandleFunction job, js::HandleObject promise,
|
||||
|
@ -124,7 +124,8 @@ wasm::HasStreamingSupport(JSContext* cx)
|
||||
return HasSupport(cx) &&
|
||||
cx->runtime()->offThreadPromiseState.ref().initialized() &&
|
||||
CanUseExtraThreads() &&
|
||||
cx->runtime()->consumeStreamCallback;
|
||||
cx->runtime()->consumeStreamCallback &&
|
||||
cx->runtime()->reportStreamErrorCallback;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -3091,10 +3092,18 @@ EnsureStreamSupport(JSContext* cx)
|
||||
return true;
|
||||
}
|
||||
|
||||
// This value is chosen and asserted to be disjoint from any host error code.
|
||||
static const size_t StreamOOMCode = 0;
|
||||
|
||||
static bool
|
||||
RejectWithErrorNumber(JSContext* cx, uint32_t errorNumber, Handle<PromiseObject*> promise)
|
||||
RejectWithStreamErrorNumber(JSContext* cx, size_t errorCode, Handle<PromiseObject*> promise)
|
||||
{
|
||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
|
||||
if (errorCode == StreamOOMCode) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
cx->runtime()->reportStreamErrorCallback(cx, errorCode);
|
||||
return RejectWithPendingException(cx, promise);
|
||||
}
|
||||
|
||||
@ -3108,7 +3117,7 @@ class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer
|
||||
const bool instantiate_;
|
||||
const PersistentRootedObject importObj_;
|
||||
|
||||
// Mutated on a stream thread (consumeChunk() and streamClosed()):
|
||||
// Mutated on a stream thread (consumeChunk(), streamEnd(), streamError()):
|
||||
ExclusiveStreamState streamState_;
|
||||
Bytes envBytes_; // immutable after Env state
|
||||
SectionRange codeSection_; // immutable after Env state
|
||||
@ -3117,7 +3126,7 @@ class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer
|
||||
ExclusiveBytesPtr exclusiveCodeBytesEnd_;
|
||||
Bytes tailBytes_; // immutable after Tail state
|
||||
ExclusiveStreamEndData exclusiveStreamEnd_;
|
||||
Maybe<uint32_t> streamError_;
|
||||
Maybe<size_t> streamError_;
|
||||
Atomic<bool> streamFailed_;
|
||||
Tier2Listener tier2Listener_;
|
||||
|
||||
@ -3126,7 +3135,7 @@ class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer
|
||||
UniqueChars compileError_;
|
||||
UniqueCharsVector warnings_;
|
||||
|
||||
// Called on some thread before consumeChunk() or streamClosed():
|
||||
// Called on some thread before consumeChunk(), streamEnd(), streamError()):
|
||||
|
||||
void noteResponseURLs(const char* url, const char* sourceMapUrl) override {
|
||||
if (url) {
|
||||
@ -3151,7 +3160,7 @@ class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer
|
||||
}
|
||||
|
||||
// See setClosedAndDestroyBeforeHelperThreadStarted() comment.
|
||||
bool rejectAndDestroyBeforeHelperThreadStarted(unsigned errorNumber) {
|
||||
bool rejectAndDestroyBeforeHelperThreadStarted(size_t errorNumber) {
|
||||
MOZ_ASSERT(streamState_.lock() == Env);
|
||||
MOZ_ASSERT(!streamError_);
|
||||
streamError_ = Some(errorNumber);
|
||||
@ -3167,13 +3176,13 @@ class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer
|
||||
// caller must immediately return from the stream callback.
|
||||
void setClosedAndDestroyAfterHelperThreadStarted() {
|
||||
auto streamState = streamState_.lock();
|
||||
MOZ_ASSERT(streamState != Closed);
|
||||
streamState.get() = Closed;
|
||||
streamState.notify_one(/* stream closed */);
|
||||
}
|
||||
|
||||
// See setClosedAndDestroyAfterHelperThreadStarted() comment.
|
||||
bool rejectAndDestroyAfterHelperThreadStarted(unsigned errorNumber) {
|
||||
MOZ_ASSERT(streamState_.lock() == Code || streamState_.lock() == Tail);
|
||||
bool rejectAndDestroyAfterHelperThreadStarted(size_t errorNumber) {
|
||||
MOZ_ASSERT(!streamError_);
|
||||
streamError_ = Some(errorNumber);
|
||||
streamFailed_ = true;
|
||||
@ -3187,7 +3196,7 @@ class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer
|
||||
switch (streamState_.lock().get()) {
|
||||
case Env: {
|
||||
if (!envBytes_.append(begin, length)) {
|
||||
return rejectAndDestroyBeforeHelperThreadStarted(JSMSG_OUT_OF_MEMORY);
|
||||
return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
|
||||
}
|
||||
|
||||
if (!StartsCodeSection(envBytes_.begin(), envBytes_.end(), &codeSection_)) {
|
||||
@ -3200,18 +3209,18 @@ class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer
|
||||
}
|
||||
|
||||
if (codeSection_.size > MaxCodeSectionBytes) {
|
||||
return rejectAndDestroyBeforeHelperThreadStarted(JSMSG_OUT_OF_MEMORY);
|
||||
return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
|
||||
}
|
||||
|
||||
if (!codeBytes_.resize(codeSection_.size)) {
|
||||
return rejectAndDestroyBeforeHelperThreadStarted(JSMSG_OUT_OF_MEMORY);
|
||||
return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
|
||||
}
|
||||
|
||||
codeBytesEnd_ = codeBytes_.begin();
|
||||
exclusiveCodeBytesEnd_.lock().get() = codeBytesEnd_;
|
||||
|
||||
if (!StartOffThreadPromiseHelperTask(this)) {
|
||||
return rejectAndDestroyBeforeHelperThreadStarted(JSMSG_OUT_OF_MEMORY);
|
||||
return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
|
||||
}
|
||||
|
||||
// Set the state to Code iff StartOffThreadPromiseHelperTask()
|
||||
@ -3250,7 +3259,7 @@ class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer
|
||||
}
|
||||
case Tail: {
|
||||
if (!tailBytes_.append(begin, length)) {
|
||||
return rejectAndDestroyAfterHelperThreadStarted(JSMSG_OUT_OF_MEMORY);
|
||||
return rejectAndDestroyAfterHelperThreadStarted(StreamOOMCode);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -3261,15 +3270,12 @@ class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer
|
||||
MOZ_CRASH("unreachable");
|
||||
}
|
||||
|
||||
void streamClosed(JS::StreamConsumer::CloseReason closeReason,
|
||||
JS::OptimizedEncodingListener* tier2Listener) override {
|
||||
switch (closeReason) {
|
||||
case JS::StreamConsumer::EndOfFile:
|
||||
void streamEnd(JS::OptimizedEncodingListener* tier2Listener) override {
|
||||
switch (streamState_.lock().get()) {
|
||||
case Env: {
|
||||
SharedBytes bytecode = js_new<ShareableBytes>(std::move(envBytes_));
|
||||
if (!bytecode) {
|
||||
rejectAndDestroyBeforeHelperThreadStarted(JSMSG_OUT_OF_MEMORY);
|
||||
rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
|
||||
return;
|
||||
}
|
||||
module_ = CompileBuffer(*compileArgs_, *bytecode, &compileError_, &warnings_);
|
||||
@ -3289,24 +3295,23 @@ class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer
|
||||
setClosedAndDestroyAfterHelperThreadStarted();
|
||||
return;
|
||||
case Closed:
|
||||
MOZ_CRASH("streamClosed() in Closed state");
|
||||
MOZ_CRASH("streamEnd() in Closed state");
|
||||
}
|
||||
break;
|
||||
case JS::StreamConsumer::Error:
|
||||
}
|
||||
|
||||
void streamError(size_t errorCode) override {
|
||||
MOZ_ASSERT(errorCode != StreamOOMCode);
|
||||
switch (streamState_.lock().get()) {
|
||||
case Env:
|
||||
rejectAndDestroyBeforeHelperThreadStarted(JSMSG_WASM_STREAM_ERROR);
|
||||
rejectAndDestroyBeforeHelperThreadStarted(errorCode);
|
||||
return;
|
||||
case Tail:
|
||||
case Code:
|
||||
rejectAndDestroyAfterHelperThreadStarted(JSMSG_WASM_STREAM_ERROR);
|
||||
rejectAndDestroyAfterHelperThreadStarted(errorCode);
|
||||
return;
|
||||
case Closed:
|
||||
MOZ_CRASH("streamClosed() in Closed state");
|
||||
MOZ_CRASH("streamError() in Closed state");
|
||||
}
|
||||
break;
|
||||
}
|
||||
MOZ_CRASH("unreachable");
|
||||
}
|
||||
|
||||
void consumeOptimizedEncoding(const uint8_t* begin, size_t length) override {
|
||||
@ -3331,7 +3336,7 @@ class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer
|
||||
// When execute() returns, the CompileStreamTask will be dispatched
|
||||
// back to its JS thread to call resolve() and then be destroyed. We
|
||||
// can't let this happen until the stream has been closed lest
|
||||
// consumeChunk() or streamClosed() be called on a dead object.
|
||||
// consumeChunk() or streamEnd() be called on a dead object.
|
||||
auto streamState = streamState_.lock();
|
||||
while (streamState != Closed) {
|
||||
streamState.wait(/* stream closed */);
|
||||
@ -3346,7 +3351,7 @@ class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer
|
||||
return module_
|
||||
? Resolve(cx, *module_, promise, instantiate_, importObj_, warnings_)
|
||||
: streamError_
|
||||
? RejectWithErrorNumber(cx, *streamError_, promise)
|
||||
? RejectWithStreamErrorNumber(cx, *streamError_, promise)
|
||||
: Reject(cx, *compileArgs_, promise, compileError_);
|
||||
}
|
||||
|
||||
@ -3448,6 +3453,13 @@ ToResolveResponseClosure(CallArgs args)
|
||||
return &args.callee().as<JSFunction>().getExtendedSlot(0).toObject().as<ResolveResponseClosure>();
|
||||
}
|
||||
|
||||
static bool
|
||||
RejectWithErrorNumber(JSContext* cx, uint32_t errorNumber, Handle<PromiseObject*> promise)
|
||||
{
|
||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
|
||||
return RejectWithPendingException(cx, promise);
|
||||
}
|
||||
|
||||
static bool
|
||||
ResolveResponse_OnFulfilled(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
|
@ -21,4 +21,17 @@ for (const method of methods) {
|
||||
controller.abort();
|
||||
return promise_rejects(t, 'AbortError', promise, `${method} should reject`);
|
||||
}, `${method}() synchronously followed by abort should reject with AbortError`);
|
||||
|
||||
promise_test(async t => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
return fetch('../incrementer.wasm', { signal })
|
||||
.then(response => {
|
||||
Promise.resolve().then(() => controller.abort());
|
||||
return WebAssembly[method](response);
|
||||
})
|
||||
.catch(err => {
|
||||
assert_true(err.name === "AbortError");
|
||||
});
|
||||
}, `${method}() asynchronously racing with abort should succeed or reject with AbortError`);
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ static const size_t kExceptionAppMemoryRegions = 16;
|
||||
|
||||
#if defined(_M_IX86)
|
||||
using RegisterValueType = DWORD;
|
||||
#elif defined(_M_AMD64)
|
||||
#elif defined(_M_AMD64) || defined(_M_ARM64)
|
||||
using RegisterValueType = DWORD64;
|
||||
#else
|
||||
#error Unsupported platform
|
||||
|
Loading…
Reference in New Issue
Block a user