Skip to content

Commit 259c341

Browse files
surmatorch2424
andauthored
Pin non-primitive function parameters (#87)
* Trying to build a repro case * Use Rehkitz’ repro * Fix it * Getting travis to run after .org to .com migration * Travis Please! * Updated the webhook, maybe now travis will update? * Next day, maybe Travis will be happy now? * Travis should work for sure now :) Co-authored-by: Aaron Turner <[email protected]>
1 parent 49cdef4 commit 259c341

File tree

6 files changed

+99
-8
lines changed

6 files changed

+99
-8
lines changed

examples/quickstart/package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/asbind-instance/bind-function.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,25 @@ export function bindExportFunction(
7171
`Expected ${argumentConverterFunctions.length} arguments, got ${args.length}`
7272
);
7373
}
74-
const newArgs = args.map((v, i) =>
75-
argumentConverterFunctions[i](
74+
// The conversion function of the _next_ parameter could potentially
75+
// trigger GC and collect the previous parameter before we even invoke
76+
// the actual function. That’s bad! We’ll pin all non-primitive parameters before invocation
77+
// and unpin them after.
78+
const pinnedArgs = [];
79+
const newArgs = args.map((originalParameter, i) => {
80+
const convertedParameter = argumentConverterFunctions[i](
7681
asbindInstance,
77-
v,
82+
originalParameter,
7883
exportedFunctionDescriptor.parameters[i]
79-
)
80-
);
84+
);
85+
if (convertedParameter !== originalParameter) {
86+
asbindInstance.exports.__pin(convertedParameter);
87+
pinnedArgs.push(convertedParameter);
88+
}
89+
return convertedParameter;
90+
});
8191
const result = exportedFunction(...newArgs);
92+
pinnedArgs.forEach(arg => asbindInstance.exports.__unpin(arg));
8293
return returnValueConverterFunction(
8394
asbindInstance,
8495
result,

test/test-runner.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const { promisify } = require("util");
22
const fs = require("fs/promises");
3+
const { dirname, join } = require("path");
34

45
const Express = require("express");
56
const Mocha = require("mocha");
@@ -31,8 +32,17 @@ async function compileAllAsc() {
3132
const ascFiles = await glob("./tests/**/asc.ts");
3233
const transformFile = require.resolve("../dist/transform.cjs.js");
3334
for (const ascFile of ascFiles) {
35+
const dir = dirname(ascFile);
36+
let config = {
37+
mangleCompilerParams() {}
38+
};
39+
try {
40+
const configPath = require.resolve("./" + join(dir, "config.js"));
41+
const m = require(configPath);
42+
Object.assign(config, m);
43+
} catch (e) {}
3444
console.log(`Compiling ${ascFile}...`);
35-
await asc.main([
45+
const params = [
3646
"--runtime",
3747
"stub",
3848
"--exportRuntime",
@@ -41,7 +51,9 @@ async function compileAllAsc() {
4151
"--binaryFile",
4252
ascFile.replace(/\.ts$/, ".wasm"),
4353
ascFile
44-
]);
54+
];
55+
config.mangleCompilerParams(params);
56+
await asc.main(params);
4557
}
4658
}
4759

test/tests/pinning/asc.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// The entry file of your WebAssembly module.
2+
3+
declare function string_log(str: string): void;
4+
5+
class MemoryTrash {
6+
public t1: string;
7+
public t2: string;
8+
public t3: i32;
9+
10+
constructor() {
11+
this.t1 = "trash1";
12+
this.t2 = "trash1";
13+
this.t3 = 42;
14+
}
15+
}
16+
17+
export function trash(amount: i32): void {
18+
for (let i = 0; i < amount; i++) {
19+
let t = new MemoryTrash();
20+
}
21+
}
22+
23+
export function string_parameter(
24+
s1: string,
25+
s2: string,
26+
s3: string,
27+
s4: string
28+
): void {
29+
string_log(s1);
30+
string_log(s2);
31+
string_log(s3);
32+
string_log(s4);
33+
}

test/tests/pinning/config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
exports.mangleCompilerParams = params => {
2+
// Remove runtime parameter
3+
const idx = params.indexOf("stub");
4+
params.splice(idx - 1, 2);
5+
// Add debug
6+
params.unshift("--target", "debug");
7+
console.log({ params });
8+
};

test/tests/pinning/test.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
describe("as-bind", function() {
2+
// Shoutout to @RehkitzDev for the repro
3+
it("should not GC strings", function(done) {
4+
let num_logs = 0;
5+
function string_log(s) {
6+
num_logs += 1;
7+
assert(!/[^ABCD]/.test(s));
8+
if (num_logs === 4) {
9+
done();
10+
}
11+
}
12+
13+
(async () => {
14+
const instance = await AsBind.instantiate(this.rawModule, {
15+
asc: { string_log }
16+
});
17+
18+
instance.exports.trash(10000);
19+
let a = "A".repeat(30);
20+
let b = "B".repeat(30);
21+
let c = "C".repeat(60);
22+
let d = "D".repeat(60);
23+
24+
instance.exports.string_parameter(a, b, c, d);
25+
})();
26+
});
27+
});

0 commit comments

Comments
 (0)