-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathpatchWebAssembly.ts
More file actions
141 lines (128 loc) · 4.85 KB
/
Copy pathpatchWebAssembly.ts
File metadata and controls
141 lines (128 loc) · 4.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import { getWasmSourceUrl, patchWasmResponseBodyReaders } from './patchWasmResponse';
export type RegisterModuleCallback = (module: WebAssembly.Module, url: string) => void;
let nonStreamingPatched = false;
/**
* Patches the WebAssembly streaming APIs so that every compiled module gets
* registered as a debug image under the URL of the response it was compiled
* from.
*
* @param registerModule callback invoked for every successfully compiled module
*/
function patchStreamingWebAssembly(registerModule: RegisterModuleCallback): void {
if ('instantiateStreaming' in WebAssembly) {
const origInstantiateStreaming = WebAssembly.instantiateStreaming as (
response: unknown,
...rest: unknown[]
) => Promise<WebAssembly.WebAssemblyInstantiatedSource>;
WebAssembly.instantiateStreaming = function instantiateStreaming(
response: Response | PromiseLike<Response>,
...rest: unknown[]
): Promise<WebAssembly.WebAssemblyInstantiatedSource> {
return Promise.resolve(response).then(response => {
return origInstantiateStreaming(response, ...rest).then(rv => {
if (response.url) {
registerSafely(registerModule, rv.module, response.url);
}
return rv;
});
});
};
}
if ('compileStreaming' in WebAssembly) {
const origCompileStreaming = WebAssembly.compileStreaming as (
source: unknown,
...rest: unknown[]
) => Promise<WebAssembly.Module>;
WebAssembly.compileStreaming = function compileStreaming(
source: Response | PromiseLike<Response>,
...rest: unknown[]
): Promise<WebAssembly.Module> {
return Promise.resolve(source).then(response => {
return origCompileStreaming(response, ...rest).then(module => {
if (response.url) {
registerSafely(registerModule, module, response.url);
}
return module;
});
});
};
}
}
function registerSafely(registerModule: RegisterModuleCallback, module: WebAssembly.Module, url: string): void {
try {
registerModule(module, url);
} catch {
// a registration failure must never break the user's WebAssembly call
}
}
/**
* Registers a module compiled from bytes under the URL those bytes were fetched from, when known.
* Runs inside the caller's promise chain, so nothing in here may throw.
*/
function registerFromBufferSource(
registerModule: RegisterModuleCallback,
compiled: WebAssembly.Module | WebAssembly.WebAssemblyInstantiatedSource | WebAssembly.Instance,
source: unknown,
): void {
try {
// `instantiate(module)` resolves to a bare Instance, which carries nothing new to register
const module =
compiled instanceof WebAssembly.Module ? compiled : 'module' in compiled ? compiled.module : undefined;
const url = getWasmSourceUrl(source);
if (module && url) {
registerModule(module, url);
}
} catch {
// a registration failure must never break the user's WebAssembly call
}
}
/**
* Patches the non-streaming web assembly runtime.
*/
function patchNonStreamingWebAssembly(registerModule: RegisterModuleCallback): void {
if (nonStreamingPatched) {
return;
}
nonStreamingPatched = true;
// Double-cast, because the overloaded native signature (buffer vs. module
// first argument) cannot be widened to a pass-through shape in one step.
const origInstantiate = WebAssembly.instantiate as unknown as (
source: unknown,
...rest: unknown[]
) => Promise<WebAssembly.WebAssemblyInstantiatedSource | WebAssembly.Instance>;
WebAssembly.instantiate = function instantiate(source: unknown, ...rest: unknown[]) {
return origInstantiate(source, ...rest).then(result => {
registerFromBufferSource(registerModule, result, source);
return result;
});
} as typeof WebAssembly.instantiate;
const origCompile = WebAssembly.compile as (source: unknown, ...rest: unknown[]) => Promise<WebAssembly.Module>;
WebAssembly.compile = function compile(source: BufferSource, ...rest: unknown[]): Promise<WebAssembly.Module> {
return origCompile(source, ...rest).then(module => {
registerFromBufferSource(registerModule, module, source);
return module;
});
};
}
/**
* Patches the web assembly runtime.
*
* Every patch is guarded on its own: a missing or frozen global must neither throw out of
* `Sentry.init()` / `registerWebWorkerWasm()` nor keep the remaining patches from installing.
*/
export function patchWebAssembly(registerModule: RegisterModuleCallback): void {
tryPatch(() => patchWasmResponseBodyReaders());
tryPatch(() => patchNonStreamingWebAssembly(registerModule));
tryPatch(() => patchStreamingWebAssembly(registerModule));
}
function tryPatch(patch: () => void): void {
try {
patch();
} catch {
// see patchWebAssembly()
}
}
/** @internal */
export function _resetNonStreamingPatchForTests(): void {
nonStreamingPatched = false;
}