Skip to content

Commit 2e6cff4

Browse files
committed
Added browser demo for fetch() via dart:js_interop_unsafe.
1 parent c9a6b01 commit 2e6cff4

File tree

4 files changed

+343
-0
lines changed

4 files changed

+343
-0
lines changed

index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ <h2>HTTP request from inside WASM in Dart - list of demos</h2>
1414
<li><a href="package-http/">Demo using package:http</a></li>
1515
<li><a href="package-requests/">Demo using package:requests</a></li>
1616
<li><a href="package-web/">Demo using package:web and fetch()</a></li>
17+
<li><a href="js-interop-fetch/">Demo using raw fetch() via js_interop</a></li>
1718
</ol>
1819

1920
</body>

js-interop-fetch/index.html

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<title>HTTP request from WASM in Dart using dart.library.js_interop and fetch()</title>
7+
<script type="module">
8+
import { instantiate, invoke } from "./main.mjs";
9+
10+
const module = await WebAssembly.compileStreaming(fetch('main.wasm'));
11+
const instance = await instantiate(module);
12+
invoke(instance);
13+
</script>
14+
</head>
15+
16+
<body>
17+
<h2>HTTP Request from inside WASM in Dart using raw <code>fetch()</code> imported via <code>js_interop</code></h2>
18+
19+
<p>This example uses imported via <code>dart:js_interop</code> raw <code>fetch()</code> function, and gets content via <code>dart:js_interop_unsafe</code>.</p>
20+
21+
<p>See the output in browser developer console.</p>
22+
23+
<p>Actual code:</p>
24+
<pre>
25+
26+
import 'dart:js_interop';
27+
import 'dart:js_interop_unsafe';
28+
29+
@JS()
30+
external JSPromise<JSObject> fetch(JSString resource);
31+
32+
final resp = await fetch('https://httpbin.org/anything'.toJS).toDart;
33+
final txt = await resp.callMethod<JSPromise<JSString>>('text'.toJS).toDart;
34+
print('body: ${txt}');
35+
36+
</pre>
37+
<section>
38+
<h4>Info</h4>
39+
<ul>
40+
<li><a href="https://github.com/wasm-outbound-http-examples/dart/blob/main/browser-and-node-js_interop_unsafe-fetch/">Demo
41+
source code</a></li>
42+
<li>Versions used on the moment of build:<small>
43+
Dart SDK: <a href="https://github.com/dart-lang/sdk/releases/tag/3.5.2">v3.5.2</a>.
44+
</small></li>
45+
</ul>
46+
</section>
47+
<footer><small>Created for (wannabe-awesome) <a href="https://github.com/vasilev/HTTP-request-from-inside-WASM">list</a></small></footer>
48+
</body>
49+
</html>

js-interop-fetch/main.mjs

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
2+
// `modulePromise` is a promise to the `WebAssembly.module` object to be
3+
// instantiated.
4+
// `importObjectPromise` is a promise to an object that contains any additional
5+
// imports needed by the module that aren't provided by the standard runtime.
6+
// The fields on this object will be merged into the importObject with which
7+
// the module will be instantiated.
8+
// This function returns a promise to the instantiated module.
9+
export const instantiate = async (modulePromise, importObjectPromise) => {
10+
let dartInstance;
11+
12+
// Prints to the console
13+
function printToConsole(value) {
14+
if (typeof dartPrint == "function") {
15+
dartPrint(value);
16+
return;
17+
}
18+
if (typeof console == "object" && typeof console.log != "undefined") {
19+
console.log(value);
20+
return;
21+
}
22+
if (typeof print == "function") {
23+
print(value);
24+
return;
25+
}
26+
27+
throw "Unable to print message: " + js;
28+
}
29+
30+
// Converts a Dart List to a JS array. Any Dart objects will be converted, but
31+
// this will be cheap for JSValues.
32+
function arrayFromDartList(constructor, list) {
33+
const exports = dartInstance.exports;
34+
const read = exports.$listRead;
35+
const length = exports.$listLength(list);
36+
const array = new constructor(length);
37+
for (let i = 0; i < length; i++) {
38+
array[i] = read(list, i);
39+
}
40+
return array;
41+
}
42+
43+
// A special symbol attached to functions that wrap Dart functions.
44+
const jsWrappedDartFunctionSymbol = Symbol("JSWrappedDartFunction");
45+
46+
function finalizeWrapper(dartFunction, wrapped) {
47+
wrapped.dartFunction = dartFunction;
48+
wrapped[jsWrappedDartFunctionSymbol] = true;
49+
return wrapped;
50+
}
51+
52+
// Imports
53+
const dart2wasm = {
54+
55+
_19: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._19(f,arguments.length,x0) }),
56+
_20: f => finalizeWrapper(f, function(x0) { return dartInstance.exports._20(f,arguments.length,x0) }),
57+
_49: v => v.toString(),
58+
_65: () => {
59+
let stackString = new Error().stack.toString();
60+
let frames = stackString.split('\n');
61+
let drop = 2;
62+
if (frames[0] === 'Error') {
63+
drop += 1;
64+
}
65+
return frames.slice(drop).join('\n');
66+
},
67+
_85: s => JSON.stringify(s),
68+
_86: s => printToConsole(s),
69+
_87: a => a.join(''),
70+
_97: (s, p, i) => s.indexOf(p, i),
71+
_103: (a, i) => a.push(i),
72+
_114: a => a.length,
73+
_116: (a, i) => a[i],
74+
_117: (a, i, v) => a[i] = v,
75+
_120: (o, start, length) => new Uint8Array(o.buffer, o.byteOffset + start, length),
76+
_121: (o, start, length) => new Int8Array(o.buffer, o.byteOffset + start, length),
77+
_122: (o, start, length) => new Uint8ClampedArray(o.buffer, o.byteOffset + start, length),
78+
_123: (o, start, length) => new Uint16Array(o.buffer, o.byteOffset + start, length),
79+
_124: (o, start, length) => new Int16Array(o.buffer, o.byteOffset + start, length),
80+
_125: (o, start, length) => new Uint32Array(o.buffer, o.byteOffset + start, length),
81+
_126: (o, start, length) => new Int32Array(o.buffer, o.byteOffset + start, length),
82+
_129: (o, start, length) => new Float32Array(o.buffer, o.byteOffset + start, length),
83+
_130: (o, start, length) => new Float64Array(o.buffer, o.byteOffset + start, length),
84+
_133: (o) => new DataView(o.buffer, o.byteOffset, o.byteLength),
85+
_137: Function.prototype.call.bind(Object.getOwnPropertyDescriptor(DataView.prototype, 'byteLength').get),
86+
_138: (b, o) => new DataView(b, o),
87+
_140: Function.prototype.call.bind(DataView.prototype.getUint8),
88+
_142: Function.prototype.call.bind(DataView.prototype.getInt8),
89+
_144: Function.prototype.call.bind(DataView.prototype.getUint16),
90+
_146: Function.prototype.call.bind(DataView.prototype.getInt16),
91+
_148: Function.prototype.call.bind(DataView.prototype.getUint32),
92+
_150: Function.prototype.call.bind(DataView.prototype.getInt32),
93+
_156: Function.prototype.call.bind(DataView.prototype.getFloat32),
94+
_158: Function.prototype.call.bind(DataView.prototype.getFloat64),
95+
_182: (c) =>
96+
queueMicrotask(() => dartInstance.exports.$invokeCallback(c)),
97+
_196: o => o === undefined,
98+
_197: o => typeof o === 'boolean',
99+
_198: o => typeof o === 'number',
100+
_200: o => typeof o === 'string',
101+
_203: o => o instanceof Int8Array,
102+
_204: o => o instanceof Uint8Array,
103+
_205: o => o instanceof Uint8ClampedArray,
104+
_206: o => o instanceof Int16Array,
105+
_207: o => o instanceof Uint16Array,
106+
_208: o => o instanceof Int32Array,
107+
_209: o => o instanceof Uint32Array,
108+
_210: o => o instanceof Float32Array,
109+
_211: o => o instanceof Float64Array,
110+
_212: o => o instanceof ArrayBuffer,
111+
_213: o => o instanceof DataView,
112+
_214: o => o instanceof Array,
113+
_215: o => typeof o === 'function' && o[jsWrappedDartFunctionSymbol] === true,
114+
_219: (l, r) => l === r,
115+
_220: o => o,
116+
_221: o => o,
117+
_222: o => o,
118+
_223: b => !!b,
119+
_224: o => o.length,
120+
_227: (o, i) => o[i],
121+
_228: f => f.dartFunction,
122+
_229: l => arrayFromDartList(Int8Array, l),
123+
_230: (data, length) => {
124+
const jsBytes = new Uint8Array(length);
125+
const getByte = dartInstance.exports.$uint8ListGet;
126+
for (let i = 0; i < length; i++) {
127+
jsBytes[i] = getByte(data, i);
128+
}
129+
return jsBytes;
130+
},
131+
_231: l => arrayFromDartList(Uint8ClampedArray, l),
132+
_232: l => arrayFromDartList(Int16Array, l),
133+
_233: l => arrayFromDartList(Uint16Array, l),
134+
_234: l => arrayFromDartList(Int32Array, l),
135+
_235: l => arrayFromDartList(Uint32Array, l),
136+
_236: l => arrayFromDartList(Float32Array, l),
137+
_237: l => arrayFromDartList(Float64Array, l),
138+
_238: (data, length) => {
139+
const read = dartInstance.exports.$byteDataGetUint8;
140+
const view = new DataView(new ArrayBuffer(length));
141+
for (let i = 0; i < length; i++) {
142+
view.setUint8(i, read(data, i));
143+
}
144+
return view;
145+
},
146+
_239: l => arrayFromDartList(Array, l),
147+
_240: (s, length) => {
148+
if (length == 0) return '';
149+
150+
const read = dartInstance.exports.$stringRead1;
151+
let result = '';
152+
let index = 0;
153+
const chunkLength = Math.min(length - index, 500);
154+
let array = new Array(chunkLength);
155+
while (index < length) {
156+
const newChunkLength = Math.min(length - index, 500);
157+
for (let i = 0; i < newChunkLength; i++) {
158+
array[i] = read(s, index++);
159+
}
160+
if (newChunkLength < chunkLength) {
161+
array = array.slice(0, newChunkLength);
162+
}
163+
result += String.fromCharCode(...array);
164+
}
165+
return result;
166+
}
167+
,
168+
_241: (s, length) => {
169+
if (length == 0) return '';
170+
171+
const read = dartInstance.exports.$stringRead2;
172+
let result = '';
173+
let index = 0;
174+
const chunkLength = Math.min(length - index, 500);
175+
let array = new Array(chunkLength);
176+
while (index < length) {
177+
const newChunkLength = Math.min(length - index, 500);
178+
for (let i = 0; i < newChunkLength; i++) {
179+
array[i] = read(s, index++);
180+
}
181+
if (newChunkLength < chunkLength) {
182+
array = array.slice(0, newChunkLength);
183+
}
184+
result += String.fromCharCode(...array);
185+
}
186+
return result;
187+
}
188+
,
189+
_242: (s) => {
190+
let length = s.length;
191+
let range = 0;
192+
for (let i = 0; i < length; i++) {
193+
range |= s.codePointAt(i);
194+
}
195+
const exports = dartInstance.exports;
196+
if (range < 256) {
197+
if (length <= 10) {
198+
if (length == 1) {
199+
return exports.$stringAllocate1_1(s.codePointAt(0));
200+
}
201+
if (length == 2) {
202+
return exports.$stringAllocate1_2(s.codePointAt(0), s.codePointAt(1));
203+
}
204+
if (length == 3) {
205+
return exports.$stringAllocate1_3(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2));
206+
}
207+
if (length == 4) {
208+
return exports.$stringAllocate1_4(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3));
209+
}
210+
if (length == 5) {
211+
return exports.$stringAllocate1_5(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4));
212+
}
213+
if (length == 6) {
214+
return exports.$stringAllocate1_6(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5));
215+
}
216+
if (length == 7) {
217+
return exports.$stringAllocate1_7(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6));
218+
}
219+
if (length == 8) {
220+
return exports.$stringAllocate1_8(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6), s.codePointAt(7));
221+
}
222+
if (length == 9) {
223+
return exports.$stringAllocate1_9(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6), s.codePointAt(7), s.codePointAt(8));
224+
}
225+
if (length == 10) {
226+
return exports.$stringAllocate1_10(s.codePointAt(0), s.codePointAt(1), s.codePointAt(2), s.codePointAt(3), s.codePointAt(4), s.codePointAt(5), s.codePointAt(6), s.codePointAt(7), s.codePointAt(8), s.codePointAt(9));
227+
}
228+
}
229+
const dartString = exports.$stringAllocate1(length);
230+
const write = exports.$stringWrite1;
231+
for (let i = 0; i < length; i++) {
232+
write(dartString, i, s.codePointAt(i));
233+
}
234+
return dartString;
235+
} else {
236+
const dartString = exports.$stringAllocate2(length);
237+
const write = exports.$stringWrite2;
238+
for (let i = 0; i < length; i++) {
239+
write(dartString, i, s.charCodeAt(i));
240+
}
241+
return dartString;
242+
}
243+
}
244+
,
245+
_245: l => new Array(l),
246+
_249: (o, p) => o[p],
247+
_251: (o, m, a) => o[m].apply(o, a),
248+
_253: o => String(o),
249+
_254: (p, s, f) => p.then(s, f),
250+
_275: x0 => globalThis.fetch(x0)
251+
};
252+
253+
const baseImports = {
254+
dart2wasm: dart2wasm,
255+
256+
257+
Math: Math,
258+
Date: Date,
259+
Object: Object,
260+
Array: Array,
261+
Reflect: Reflect,
262+
};
263+
264+
const jsStringPolyfill = {
265+
"charCodeAt": (s, i) => s.charCodeAt(i),
266+
"compare": (s1, s2) => {
267+
if (s1 < s2) return -1;
268+
if (s1 > s2) return 1;
269+
return 0;
270+
},
271+
"concat": (s1, s2) => s1 + s2,
272+
"equals": (s1, s2) => s1 === s2,
273+
"fromCharCode": (i) => String.fromCharCode(i),
274+
"length": (s) => s.length,
275+
"substring": (s, a, b) => s.substring(a, b),
276+
};
277+
278+
dartInstance = await WebAssembly.instantiate(await modulePromise, {
279+
...baseImports,
280+
...(await importObjectPromise),
281+
"wasm:js-string": jsStringPolyfill,
282+
});
283+
284+
return dartInstance;
285+
}
286+
287+
// Call the main function for the instantiated module
288+
// `moduleInstance` is the instantiated dart2wasm module
289+
// `args` are any arguments that should be passed into the main function.
290+
export const invoke = (moduleInstance, ...args) => {
291+
moduleInstance.exports.$invokeMain(args);
292+
}
293+

js-interop-fetch/main.wasm

58.7 KB
Binary file not shown.

0 commit comments

Comments
 (0)