Remediate top level async calls. (#1375)
* Remediate top level async calls. * Prefer null over undefined.
This commit is contained in:
@@ -17,15 +17,18 @@ import {strict as assert} from 'assert';
|
||||
import {afterEach, expect, suite, test} from 'vitest';
|
||||
|
||||
import {readMesh, setupIO} from '../lib/gltf-io';
|
||||
import {getManifoldModule} from '../lib/wasm.js';
|
||||
import type {ManifoldToplevel} from '../manifold.d.ts';
|
||||
|
||||
// @ts-ignore
|
||||
import {examples} from './public/examples.js';
|
||||
import {Mesh} from './public/manifold';
|
||||
import {cleanup, evaluateCADToModel, module} from './worker';
|
||||
import {cleanup, evaluateCADToModel} from './worker';
|
||||
|
||||
const io = setupIO(new WebIO());
|
||||
|
||||
async function runExample(name: string) {
|
||||
const module: ManifoldToplevel = await getManifoldModule();
|
||||
const code = examples.functionBodies.get(name);
|
||||
const result = await evaluateCADToModel(code);
|
||||
cleanup();
|
||||
|
||||
@@ -29,7 +29,6 @@ function log(...args: any[]) {
|
||||
|
||||
// Setup the evaluator and it's context.
|
||||
const evaluator = new Evaluator();
|
||||
export const module = evaluator.getModule(); // Used in tests.
|
||||
|
||||
// Exporters.
|
||||
// The end user can download either.
|
||||
|
||||
@@ -14,11 +14,8 @@
|
||||
|
||||
import {CrossSection, Manifold} from '../manifold-encapsulated-types';
|
||||
import type {ManifoldToplevel} from '../manifold.d.ts';
|
||||
import Module from '../manifold.js';
|
||||
|
||||
// Instantiate Manifold WASM
|
||||
const manifoldwasm = await Module();
|
||||
manifoldwasm.setup();
|
||||
import {getManifoldModule} from './wasm.ts';
|
||||
|
||||
// manifold static methods (that return a new manifold)
|
||||
const manifoldStaticFunctions = [
|
||||
@@ -102,44 +99,34 @@ export class Evaluator {
|
||||
afterScript: string =
|
||||
'return typeof result === "undefined" ? undefined : result;';
|
||||
|
||||
protected module: ManifoldToplevel = manifoldwasm;
|
||||
protected module: ManifoldToplevel|undefined;
|
||||
protected memoryRegistry: Array<Manifold|CrossSection>;
|
||||
protected moduleIntercepted: boolean = false;
|
||||
|
||||
/**
|
||||
* Construct a new evaluator.
|
||||
*
|
||||
* @param module An optional Manifold module instance.
|
||||
*/
|
||||
constructor() {
|
||||
constructor(module?: ManifoldToplevel) {
|
||||
this.memoryRegistry = new Array<Manifold|CrossSection>();
|
||||
|
||||
this.addMembers('Manifold', manifoldMemberFunctions, false);
|
||||
this.addMembers('Manifold', manifoldStaticFunctions, true);
|
||||
this.addMembers('CrossSection', crossSectionMemberFunctions, false);
|
||||
this.addMembers('CrossSection', crossSectionStaticFunctions, true);
|
||||
if (module) {
|
||||
this.module = module;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept calls and add their results to our garbage collection
|
||||
* list.
|
||||
* Pass an existing, instantiated manifold module to this evaluator.
|
||||
*
|
||||
* @param className The class to intercept.
|
||||
* @param methodNames An array of methods to intercept.
|
||||
* @param areStatic Are these static methods? If so, intercept them at
|
||||
* the prototype level.
|
||||
* @param module The manifold module to use.
|
||||
*/
|
||||
protected addMembers(
|
||||
className: string, methodNames: Array<string>, areStatic: boolean) {
|
||||
//@ts-ignore
|
||||
const cls = this.module[className];
|
||||
const obj = areStatic ? cls : cls.prototype;
|
||||
for (const name of methodNames) {
|
||||
const originalFn = obj[name];
|
||||
obj[name] = (...args: any) => {
|
||||
//@ts-ignore
|
||||
const result = originalFn(...args);
|
||||
this.memoryRegistry.push(result);
|
||||
return result;
|
||||
};
|
||||
setModule(module: ManifoldToplevel) {
|
||||
if (this.memoryRegistry.length) {
|
||||
throw new Error(
|
||||
'Cannot replace manifold module while there are outstanding objects to clean up.');
|
||||
}
|
||||
this.module = module;
|
||||
this.moduleIntercepted = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,6 +178,48 @@ export class Evaluator {
|
||||
this.memoryRegistry.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up garbage collection for a white listed set of methods belonging
|
||||
* to the Manifold WASM module.
|
||||
*/
|
||||
protected setupGarbageCollection(): undefined {
|
||||
if (this.moduleIntercepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.addMembers('Manifold', manifoldMemberFunctions, false);
|
||||
this.addMembers('Manifold', manifoldStaticFunctions, true);
|
||||
this.addMembers('CrossSection', crossSectionMemberFunctions, false);
|
||||
this.addMembers('CrossSection', crossSectionStaticFunctions, true);
|
||||
|
||||
this.moduleIntercepted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept calls and add their results to our garbage collection
|
||||
* list.
|
||||
*
|
||||
* @param className The class to intercept.
|
||||
* @param methodNames An array of methods to intercept.
|
||||
* @param areStatic Are these static methods? If so, intercept them at
|
||||
* the prototype level.
|
||||
*/
|
||||
protected addMembers(
|
||||
className: string, methodNames: Array<string>, areStatic: boolean) {
|
||||
//@ts-ignore
|
||||
const cls = this.module[className];
|
||||
const obj = areStatic ? cls : cls.prototype;
|
||||
for (const name of methodNames) {
|
||||
const originalFn = obj[name];
|
||||
obj[name] = (...args: any) => {
|
||||
//@ts-ignore
|
||||
const result = originalFn(...args);
|
||||
this.memoryRegistry.push(result);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate a string as javascript code creating a Manifold model.
|
||||
*
|
||||
@@ -204,6 +233,11 @@ export class Evaluator {
|
||||
* behaviour.
|
||||
*/
|
||||
async evaluate(code: string): Promise<any> {
|
||||
if (!this.module) {
|
||||
this.module = await getManifoldModule();
|
||||
}
|
||||
this.setupGarbageCollection();
|
||||
|
||||
const exposedFunctions =
|
||||
toplevel.map((name) => [name, (this.module as any)[name]]);
|
||||
const context = {
|
||||
@@ -226,6 +260,6 @@ export class Evaluator {
|
||||
* collection will continue to be intercepted, even outside of the evaluator.
|
||||
*/
|
||||
getModule(): ManifoldToplevel {
|
||||
return this.module;
|
||||
return this.module!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright 2025 The Manifold Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import type {ManifoldToplevel} from '../manifold.d.ts';
|
||||
import Module from '../manifold.js';
|
||||
|
||||
// Instantiate Manifold WASM
|
||||
let manifoldwasm: ManifoldToplevel|null = null;
|
||||
let wasmUrl: string|null = null;
|
||||
|
||||
/**
|
||||
* Tell us how to find `manifold.wasm`.
|
||||
* This is important when using a bundler like WebPack.
|
||||
*
|
||||
* @param url Path to manifold.wasm
|
||||
*/
|
||||
export function setWasmUrl(url: string) {
|
||||
wasmUrl = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a new Manifold WASM instance.
|
||||
*
|
||||
* @returns The newly created instance.
|
||||
*/
|
||||
export async function instantiateManifold(): Promise<ManifoldToplevel> {
|
||||
const options = wasmUrl ? {locateFile: () => wasmUrl!} : undefined;
|
||||
const module = await Module(options);
|
||||
module.setup();
|
||||
return module;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate or get a global Manifold WASM instance.
|
||||
*
|
||||
* @returns A manifold instance.
|
||||
*/
|
||||
export async function getManifoldModule(): Promise<ManifoldToplevel> {
|
||||
if (!manifoldwasm) manifoldwasm = await instantiateManifold();
|
||||
return manifoldwasm;
|
||||
}
|
||||
@@ -38,7 +38,9 @@
|
||||
"lib/manifold-gltf.d.ts",
|
||||
"lib/manifold-gltf.js",
|
||||
"lib/material.d.ts",
|
||||
"lib/material.js"
|
||||
"lib/material.js",
|
||||
"lib/wasm.js",
|
||||
"lib/wasm.d.ts"
|
||||
],
|
||||
"typings": "manifold.d.ts",
|
||||
"types": "manifold.d.ts",
|
||||
|
||||
Reference in New Issue
Block a user