137 lines
5.1 KiB
JavaScript
137 lines
5.1 KiB
JavaScript
// 1. +++ fetchAndInstantiate() +++ //
|
|
|
|
// This library function fetches the wasm module at 'url', instantiates it with
|
|
// the given 'importObject', and returns the instantiated object instance
|
|
|
|
export async function instantiateStreaming(url, importObject) {
|
|
let result = await WebAssembly.instantiateStreaming(fetch(url), importObject);
|
|
return result.instance;
|
|
}
|
|
export function fetchAndInstantiate(url, importObject) {
|
|
return fetch(url)
|
|
.then(response => response.arrayBuffer())
|
|
.then(bytes => WebAssembly.instantiate(bytes, importObject))
|
|
.then(results => results.instance);
|
|
}
|
|
|
|
// 2. +++ instantiateCachedURL() +++ //
|
|
|
|
// This library function fetches the wasm Module at 'url', instantiates it with
|
|
// the given 'importObject', and returns a Promise resolving to the finished
|
|
// wasm Instance. Additionally, the function attempts to cache the compiled wasm
|
|
// Module in IndexedDB using 'url' as the key. The entire site's wasm cache (not
|
|
// just the given URL) is versioned by dbVersion and any change in dbVersion on
|
|
// any call to instantiateCachedURL() will conservatively clear out the entire
|
|
// cache to avoid stale modules.
|
|
export function instantiateCachedURL(dbVersion, url, importObject) {
|
|
const dbName = 'wasm-cache';
|
|
const storeName = 'wasm-cache';
|
|
|
|
// This helper function Promise-ifies the operation of opening an IndexedDB
|
|
// database and clearing out the cache when the version changes.
|
|
function openDatabase() {
|
|
return new Promise((resolve, reject) => {
|
|
var request = indexedDB.open(dbName, dbVersion);
|
|
request.onerror = reject.bind(null, 'Error opening wasm cache database');
|
|
request.onsuccess = () => {
|
|
resolve(request.result);
|
|
};
|
|
request.onupgradeneeded = event => {
|
|
var db = request.result;
|
|
if (db.objectStoreNames.contains(storeName)) {
|
|
console.log(`Clearing out version ${event.oldVersion} wasm cache`);
|
|
db.deleteObjectStore(storeName);
|
|
}
|
|
console.log(`Creating version ${event.newVersion} wasm cache`);
|
|
db.createObjectStore(storeName);
|
|
};
|
|
});
|
|
}
|
|
|
|
// This helper function Promise-ifies the operation of looking up 'url' in the
|
|
// given IDBDatabase.
|
|
function lookupInDatabase(db) {
|
|
return new Promise((resolve, reject) => {
|
|
var store = db.transaction([storeName]).objectStore(storeName);
|
|
var request = store.get(url);
|
|
request.onerror = reject.bind(null, `Error getting wasm module ${url}`);
|
|
request.onsuccess = event => {
|
|
if (request.result) resolve(request.result);
|
|
else reject(`Module ${url} was not found in wasm cache`);
|
|
};
|
|
});
|
|
}
|
|
|
|
// This helper function fires off an async operation to store the given wasm
|
|
// Module in the given IDBDatabase.
|
|
function storeInDatabase(db, module) {
|
|
var store = db.transaction([storeName], 'readwrite').objectStore(storeName);
|
|
var request = store.put(module, url);
|
|
request.onerror = err => {
|
|
console.log(`Failed to store in wasm cache: ${err}`);
|
|
};
|
|
request.onsuccess = err => {
|
|
console.log(`Successfully stored ${url} in wasm cache`);
|
|
};
|
|
}
|
|
|
|
// This helper function fetches 'url', compiles it into a Module,
|
|
// instantiates the Module with the given import object.
|
|
function fetchAndInstantiate() {
|
|
return fetch(url)
|
|
.then(response => response.arrayBuffer())
|
|
.then(buffer => WebAssembly.instantiate(buffer, importObject));
|
|
}
|
|
|
|
// With all the Promise helper functions defined, we can now express the core
|
|
// logic of an IndexedDB cache lookup. We start by trying to open a database.
|
|
return openDatabase().then(
|
|
db => {
|
|
// Now see if we already have a compiled Module with key 'url' in 'db':
|
|
return lookupInDatabase(db).then(
|
|
module => {
|
|
// We do! Instantiate it with the given import object.
|
|
console.log(`Found ${url} in wasm cache`);
|
|
return WebAssembly.instantiate(module, importObject);
|
|
},
|
|
errMsg => {
|
|
// Nope! Compile from scratch and then store the compiled Module in 'db'
|
|
// with key 'url' for next time.
|
|
console.log(errMsg);
|
|
return fetchAndInstantiate().then(results => {
|
|
try {
|
|
storeInDatabase(db, results.module);
|
|
} catch (e) {
|
|
console.log('Failed to store module into db');
|
|
}
|
|
return results.instance;
|
|
});
|
|
}
|
|
);
|
|
},
|
|
errMsg => {
|
|
// If opening the database failed (due to permissions or quota), fall back
|
|
// to simply fetching and compiling the module and don't try to store the
|
|
// results.
|
|
console.log(errMsg);
|
|
return fetchAndInstantiate().then(results => results.instance);
|
|
}
|
|
);
|
|
}
|
|
|
|
export async function instantiateAny(version, url, importObject) {
|
|
console.log("instantiate");
|
|
try {
|
|
return await instantiateStreaming(url, importObject);
|
|
} catch (e) {
|
|
console.log("instantiateStreaming failed", e);
|
|
}
|
|
try {
|
|
return await instantiateCachedURL(version, url, importObject);
|
|
} catch (e) {
|
|
console.log("instantiateCachedURL failed", e);
|
|
}
|
|
throw new Error("can't instantiate wasm");
|
|
}
|
|
|