@agoric/swingset-liveslots 0.10.3-dev-7ffae88.0 → 0.10.3-dev-32c4d68.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/swingset-liveslots",
|
|
3
|
-
"version": "0.10.3-dev-
|
|
3
|
+
"version": "0.10.3-dev-32c4d68.0+32c4d68",
|
|
4
4
|
"description": "SwingSet ocap support layer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -17,23 +17,23 @@
|
|
|
17
17
|
"lint:eslint": "eslint ."
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@agoric/assert": "0.6.1-dev-
|
|
21
|
-
"@agoric/internal": "0.3.3-dev-
|
|
22
|
-
"@agoric/store": "0.9.3-dev-
|
|
23
|
-
"@endo/env-options": "^1.1.
|
|
24
|
-
"@endo/errors": "^1.0
|
|
25
|
-
"@endo/eventual-send": "^1.1.
|
|
26
|
-
"@endo/exo": "^1.1
|
|
27
|
-
"@endo/far": "^1.0.
|
|
28
|
-
"@endo/init": "^1.0.
|
|
29
|
-
"@endo/marshal": "^1.
|
|
30
|
-
"@endo/nat": "^5.0.
|
|
31
|
-
"@endo/pass-style": "^1.
|
|
32
|
-
"@endo/patterns": "^1.
|
|
33
|
-
"@endo/promise-kit": "^1.0.
|
|
20
|
+
"@agoric/assert": "0.6.1-dev-32c4d68.0+32c4d68",
|
|
21
|
+
"@agoric/internal": "0.3.3-dev-32c4d68.0+32c4d68",
|
|
22
|
+
"@agoric/store": "0.9.3-dev-32c4d68.0+32c4d68",
|
|
23
|
+
"@endo/env-options": "^1.1.1",
|
|
24
|
+
"@endo/errors": "^1.1.0",
|
|
25
|
+
"@endo/eventual-send": "^1.1.2",
|
|
26
|
+
"@endo/exo": "^1.2.1",
|
|
27
|
+
"@endo/far": "^1.0.4",
|
|
28
|
+
"@endo/init": "^1.0.4",
|
|
29
|
+
"@endo/marshal": "^1.3.0",
|
|
30
|
+
"@endo/nat": "^5.0.4",
|
|
31
|
+
"@endo/pass-style": "^1.2.0",
|
|
32
|
+
"@endo/patterns": "^1.2.0",
|
|
33
|
+
"@endo/promise-kit": "^1.0.4"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@agoric/kmarshal": "0.1.1-dev-
|
|
36
|
+
"@agoric/kmarshal": "0.1.1-dev-32c4d68.0+32c4d68",
|
|
37
37
|
"ava": "^5.3.0"
|
|
38
38
|
},
|
|
39
39
|
"files": [
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"access": "public"
|
|
68
68
|
},
|
|
69
69
|
"typeCoverage": {
|
|
70
|
-
"atLeast": 75.
|
|
70
|
+
"atLeast": 75.2
|
|
71
71
|
},
|
|
72
|
-
"gitHead": "
|
|
72
|
+
"gitHead": "32c4d68c5f675f1e8fea1a8bc08621816e7b491e"
|
|
73
73
|
}
|
package/test/gc-and-finalize.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* global setImmediate */
|
|
1
|
+
/* global setImmediate, FinalizationRegistry */
|
|
2
2
|
|
|
3
3
|
/* A note on our GC terminology:
|
|
4
4
|
*
|
|
@@ -89,3 +89,32 @@ export function makeGcAndFinalize(gcPower) {
|
|
|
89
89
|
await new Promise(setImmediate);
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
|
+
|
|
93
|
+
const fr = new FinalizationRegistry(({ promise, resolve }) => {
|
|
94
|
+
promise.result = true;
|
|
95
|
+
resolve(true);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const makeCollectedResultKit = () => {
|
|
99
|
+
/** @type {(val: true) => void} */
|
|
100
|
+
let resolve;
|
|
101
|
+
|
|
102
|
+
const promise = /** @type {Promise<true> & {result: boolean}} */ (
|
|
103
|
+
new Promise(r => {
|
|
104
|
+
resolve = r;
|
|
105
|
+
})
|
|
106
|
+
);
|
|
107
|
+
promise.result = false;
|
|
108
|
+
return {
|
|
109
|
+
promise,
|
|
110
|
+
// @ts-expect-error
|
|
111
|
+
resolve,
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export function watchCollected(target) {
|
|
116
|
+
const kit = makeCollectedResultKit();
|
|
117
|
+
fr.register(target, kit);
|
|
118
|
+
|
|
119
|
+
return kit.promise;
|
|
120
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
|
-
/* global
|
|
2
|
+
/* global process */
|
|
3
3
|
import test from 'ava';
|
|
4
4
|
|
|
5
5
|
import { Far } from '@endo/marshal';
|
|
6
6
|
import { makePromiseKit } from '@endo/promise-kit';
|
|
7
7
|
import { kslot, kser } from '@agoric/kmarshal';
|
|
8
8
|
import engineGC from './engine-gc.js';
|
|
9
|
-
import { makeGcAndFinalize } from './gc-and-finalize.js';
|
|
9
|
+
import { watchCollected, makeGcAndFinalize } from './gc-and-finalize.js';
|
|
10
10
|
import { buildSyscall, makeDispatch } from './liveslots-helpers.js';
|
|
11
11
|
import {
|
|
12
12
|
makeMessage,
|
|
@@ -28,13 +28,13 @@ const gcAndFinalize = makeGcAndFinalize(engineGC);
|
|
|
28
28
|
|
|
29
29
|
test.serial('liveslots retains pending exported promise', async t => {
|
|
30
30
|
const { log, syscall } = buildSyscall();
|
|
31
|
-
let
|
|
31
|
+
let collected;
|
|
32
32
|
const success = [];
|
|
33
33
|
function build(_vatPowers) {
|
|
34
34
|
const root = Far('root', {
|
|
35
35
|
make() {
|
|
36
36
|
const pk = makePromiseKit();
|
|
37
|
-
|
|
37
|
+
collected = watchCollected(pk.promise);
|
|
38
38
|
// we export the Promise, but do not retain resolve/reject
|
|
39
39
|
return [pk.promise];
|
|
40
40
|
},
|
|
@@ -55,7 +55,7 @@ test.serial('liveslots retains pending exported promise', async t => {
|
|
|
55
55
|
const resultP = 'p-1';
|
|
56
56
|
await dispatch(makeMessage(rootA, 'make', [], resultP));
|
|
57
57
|
await gcAndFinalize();
|
|
58
|
-
t.
|
|
58
|
+
t.false(collected.result, 'Promise retained');
|
|
59
59
|
t.is(log[0].type, 'resolve');
|
|
60
60
|
const res0 = log[0].resolutions[0];
|
|
61
61
|
t.is(res0[0], resultP);
|
|
@@ -66,13 +66,13 @@ test.serial('liveslots retains pending exported promise', async t => {
|
|
|
66
66
|
|
|
67
67
|
test.serial('liveslots retains device nodes', async t => {
|
|
68
68
|
const { syscall } = buildSyscall();
|
|
69
|
-
let
|
|
69
|
+
let collected;
|
|
70
70
|
const recognize = new WeakSet(); // real WeakSet
|
|
71
71
|
const success = [];
|
|
72
72
|
function build(_vatPowers) {
|
|
73
73
|
const root = Far('root', {
|
|
74
74
|
first(dn) {
|
|
75
|
-
|
|
75
|
+
collected = watchCollected(dn);
|
|
76
76
|
recognize.add(dn);
|
|
77
77
|
},
|
|
78
78
|
second(dn) {
|
|
@@ -87,25 +87,24 @@ test.serial('liveslots retains device nodes', async t => {
|
|
|
87
87
|
const device = 'd-1';
|
|
88
88
|
await dispatch(makeMessage(rootA, 'first', [kslot(device)]));
|
|
89
89
|
await gcAndFinalize();
|
|
90
|
-
t.
|
|
90
|
+
t.false(collected.result, 'Device node retained');
|
|
91
91
|
await dispatch(makeMessage(rootA, 'second', [kslot(device)]));
|
|
92
92
|
t.deepEqual(success, [true]);
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
test.serial('GC syscall.dropImports', async t => {
|
|
96
96
|
const { log, syscall } = buildSyscall();
|
|
97
|
-
let
|
|
97
|
+
let collected;
|
|
98
98
|
function build(_vatPowers) {
|
|
99
|
-
|
|
100
|
-
let presence1;
|
|
99
|
+
const holder = new Set();
|
|
101
100
|
const root = Far('root', {
|
|
102
101
|
one(arg) {
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
holder.add(arg);
|
|
103
|
+
collected = watchCollected(arg);
|
|
105
104
|
},
|
|
106
105
|
two() {},
|
|
107
106
|
three() {
|
|
108
|
-
|
|
107
|
+
holder.clear(); // drops the import
|
|
109
108
|
},
|
|
110
109
|
});
|
|
111
110
|
return root;
|
|
@@ -121,19 +120,29 @@ test.serial('GC syscall.dropImports', async t => {
|
|
|
121
120
|
// rp1 = root~.one(arg)
|
|
122
121
|
await dispatch(makeMessage(rootA, 'one', [kslot(arg)]));
|
|
123
122
|
await dispatch(makeBringOutYourDead());
|
|
124
|
-
t.
|
|
123
|
+
t.false(collected.result);
|
|
125
124
|
|
|
126
125
|
// an intermediate message will trigger GC, but the presence is still held
|
|
127
126
|
await dispatch(makeMessage(rootA, 'two', []));
|
|
128
127
|
await dispatch(makeBringOutYourDead());
|
|
129
|
-
t.
|
|
128
|
+
t.false(collected.result);
|
|
130
129
|
|
|
131
130
|
// now tell the vat to drop the 'arg' presence we gave them earlier
|
|
132
131
|
await dispatch(makeMessage(rootA, 'three', []));
|
|
133
132
|
await dispatch(makeBringOutYourDead());
|
|
134
133
|
|
|
134
|
+
const isV8 =
|
|
135
|
+
typeof process !== 'undefined' && 'v8' in (process.versions || {});
|
|
136
|
+
|
|
135
137
|
// the presence itself should be gone
|
|
136
|
-
|
|
138
|
+
if (!collected.result) {
|
|
139
|
+
if (isV8) {
|
|
140
|
+
// Flake in v8/node: https://github.com/Agoric/agoric-sdk/issues/8883
|
|
141
|
+
t.log('skipping flake in v8');
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
t.fail('import not collected');
|
|
145
|
+
}
|
|
137
146
|
|
|
138
147
|
// first it will check that there are no VO's holding onto it
|
|
139
148
|
const l2 = log.shift();
|
|
@@ -246,12 +255,12 @@ test.serial('GC dispatch.retireExports', async t => {
|
|
|
246
255
|
|
|
247
256
|
test.serial('GC dispatch.dropExports', async t => {
|
|
248
257
|
const { log, syscall } = buildSyscall();
|
|
249
|
-
let
|
|
258
|
+
let collected;
|
|
250
259
|
function build(_vatPowers) {
|
|
251
260
|
const root = Far('root', {
|
|
252
261
|
one() {
|
|
253
262
|
const ex1 = Far('export', {});
|
|
254
|
-
|
|
263
|
+
collected = watchCollected(ex1);
|
|
255
264
|
return ex1;
|
|
256
265
|
// ex1 goes out of scope, dropping last userspace strongref
|
|
257
266
|
},
|
|
@@ -285,19 +294,19 @@ test.serial('GC dispatch.dropExports', async t => {
|
|
|
285
294
|
|
|
286
295
|
// the exported Remotable should be held in place by exportedRemotables
|
|
287
296
|
// until we tell the vat we don't need it any more
|
|
288
|
-
t.
|
|
297
|
+
t.false(collected.result);
|
|
289
298
|
|
|
290
299
|
// an intermediate message will trigger GC, but the presence is still held
|
|
291
300
|
await dispatch(makeMessage(rootA, 'two', []));
|
|
292
301
|
await dispatch(makeBringOutYourDead());
|
|
293
|
-
t.
|
|
302
|
+
t.false(collected.result);
|
|
294
303
|
|
|
295
304
|
// now tell the vat we don't need a strong reference to that export.
|
|
296
305
|
await dispatch(makeDropExports(ex1));
|
|
297
306
|
await dispatch(makeBringOutYourDead());
|
|
298
307
|
|
|
299
308
|
// that should allow ex1 to be collected
|
|
300
|
-
t.
|
|
309
|
+
t.true(collected.result);
|
|
301
310
|
|
|
302
311
|
// and once it's collected, the vat should emit `syscall.retireExport`
|
|
303
312
|
// because nobody else will be able to recognize it again
|
|
@@ -313,19 +322,19 @@ test.serial(
|
|
|
313
322
|
'GC dispatch.retireExports inhibits syscall.retireExports',
|
|
314
323
|
async t => {
|
|
315
324
|
const { log, syscall } = buildSyscall();
|
|
316
|
-
let
|
|
325
|
+
let collected;
|
|
317
326
|
function build(_vatPowers) {
|
|
318
|
-
|
|
327
|
+
const holder = new Set();
|
|
319
328
|
const root = Far('root', {
|
|
320
329
|
hold() {
|
|
321
|
-
ex1 = Far('export', {});
|
|
322
|
-
|
|
330
|
+
const ex1 = Far('export', {});
|
|
331
|
+
holder.add(ex1);
|
|
332
|
+
collected = watchCollected(ex1);
|
|
323
333
|
return ex1;
|
|
324
334
|
},
|
|
325
335
|
two() {},
|
|
326
336
|
drop() {
|
|
327
|
-
//
|
|
328
|
-
ex1 = undefined; // drop the last userspace strongref
|
|
337
|
+
holder.clear(); // drop the last userspace strongref
|
|
329
338
|
},
|
|
330
339
|
});
|
|
331
340
|
return root;
|
|
@@ -356,19 +365,19 @@ test.serial(
|
|
|
356
365
|
|
|
357
366
|
// the exported Remotable should be held in place by exportedRemotables
|
|
358
367
|
// until we tell the vat we don't need it any more
|
|
359
|
-
t.
|
|
368
|
+
t.false(collected.result);
|
|
360
369
|
|
|
361
370
|
// an intermediate message will trigger GC, but the presence is still held
|
|
362
371
|
await dispatch(makeMessage(rootA, 'two', []));
|
|
363
372
|
await dispatch(makeBringOutYourDead());
|
|
364
|
-
t.
|
|
373
|
+
t.false(collected.result);
|
|
365
374
|
|
|
366
375
|
// now tell the vat we don't need a strong reference to that export.
|
|
367
376
|
await dispatch(makeDropExports(ex1));
|
|
368
377
|
await dispatch(makeBringOutYourDead());
|
|
369
378
|
|
|
370
379
|
// that removes the liveslots strongref, but the vat's remains in place
|
|
371
|
-
t.
|
|
380
|
+
t.false(collected.result);
|
|
372
381
|
|
|
373
382
|
// now the kernel tells the vat we can't even recognize the export
|
|
374
383
|
await dispatch(makeRetireExports(ex1));
|
|
@@ -376,14 +385,14 @@ test.serial(
|
|
|
376
385
|
|
|
377
386
|
// that ought to delete the table entry, but doesn't affect the vat
|
|
378
387
|
// strongref
|
|
379
|
-
t.
|
|
388
|
+
t.false(collected.result);
|
|
380
389
|
|
|
381
390
|
// now tell the vat to drop its strongref
|
|
382
391
|
await dispatch(makeMessage(rootA, 'drop', []));
|
|
383
392
|
await dispatch(makeBringOutYourDead());
|
|
384
393
|
|
|
385
394
|
// which should let the export be collected
|
|
386
|
-
t.
|
|
395
|
+
t.true(collected.result);
|
|
387
396
|
|
|
388
397
|
// the vat should *not* emit `syscall.retireExport`, because it already
|
|
389
398
|
// received a dispatch.retireExport
|
|
@@ -1,56 +1,57 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
|
-
/* global WeakRef */
|
|
3
2
|
import test from 'ava';
|
|
4
3
|
|
|
5
4
|
import { Far } from '@endo/marshal';
|
|
6
5
|
import { initEmpty } from '@agoric/store';
|
|
7
6
|
|
|
8
7
|
import engineGC from '../engine-gc.js';
|
|
9
|
-
import { makeGcAndFinalize } from '../gc-and-finalize.js';
|
|
8
|
+
import { makeGcAndFinalize, watchCollected } from '../gc-and-finalize.js';
|
|
10
9
|
import { makeFakeVirtualStuff } from '../../tools/fakeVirtualSupport.js';
|
|
11
10
|
|
|
12
|
-
function
|
|
13
|
-
const held = Far(
|
|
14
|
-
const
|
|
11
|
+
function makeStashKit(name = 'held') {
|
|
12
|
+
const held = Far(name);
|
|
13
|
+
const collected = watchCollected(held);
|
|
15
14
|
const ws = new WeakSet(); // note: real WeakSet, not vref-aware
|
|
16
15
|
ws.add(held);
|
|
17
16
|
function isHeld(obj) {
|
|
18
17
|
return ws.has(obj);
|
|
19
18
|
}
|
|
20
|
-
|
|
19
|
+
function isCollected() {
|
|
20
|
+
return collected.result;
|
|
21
|
+
}
|
|
22
|
+
return { held, isCollected, isHeld };
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
function prepareEphemeral(vom) {
|
|
24
|
-
const
|
|
25
|
-
vom.registerEntry('o+12345',
|
|
26
|
-
|
|
27
|
-
return { wr };
|
|
26
|
+
const { held, isCollected } = makeStashKit('ephemeral');
|
|
27
|
+
vom.registerEntry('o+12345', held);
|
|
28
|
+
return { isCollected };
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
function stashRemotableOne(weakStore, key1) {
|
|
31
|
-
const { held,
|
|
32
|
+
const { held, isCollected, isHeld } = makeStashKit();
|
|
32
33
|
weakStore.init(key1, held);
|
|
33
|
-
return {
|
|
34
|
+
return { isCollected, isHeld };
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
function stashRemotableTwo(weakStore, key1) {
|
|
37
|
-
const { held,
|
|
38
|
+
const { held, isCollected, isHeld } = makeStashKit();
|
|
38
39
|
weakStore.init(key1, 'initial');
|
|
39
40
|
weakStore.set(key1, held);
|
|
40
|
-
return {
|
|
41
|
+
return { isCollected, isHeld };
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
function stashRemotableThree(holderMaker) {
|
|
44
|
-
const { held,
|
|
45
|
+
const { held, isCollected, isHeld } = makeStashKit();
|
|
45
46
|
const holder = holderMaker(held);
|
|
46
|
-
return {
|
|
47
|
+
return { isCollected, isHeld, holder };
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
function stashRemotableFour(holderMaker) {
|
|
50
|
-
const { held,
|
|
51
|
+
const { held, isCollected, isHeld } = makeStashKit();
|
|
51
52
|
const holder = holderMaker('initial');
|
|
52
53
|
holder.setHeld(held);
|
|
53
|
-
return {
|
|
54
|
+
return { isCollected, isHeld, holder };
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
test('remotables retained by virtualized data', async t => {
|
|
@@ -74,7 +75,7 @@ test('remotables retained by virtualized data', async t => {
|
|
|
74
75
|
// false positive in the subsequent test
|
|
75
76
|
const stash0 = prepareEphemeral(vom);
|
|
76
77
|
await gcAndFinalize();
|
|
77
|
-
t.
|
|
78
|
+
t.true(stash0.isCollected(), `caution: fake VOM didn't release Remotable`);
|
|
78
79
|
|
|
79
80
|
// stash a Remotable in the value of a weakStore
|
|
80
81
|
const key1 = makeKey();
|
|
@@ -84,14 +85,14 @@ test('remotables retained by virtualized data', async t => {
|
|
|
84
85
|
// Representatives or Presences, so the value is not holding a strong
|
|
85
86
|
// reference to the Remotable. The VOM is supposed to keep it alive, via
|
|
86
87
|
// reachableRemotables.
|
|
87
|
-
t.
|
|
88
|
+
t.false(stash1.isCollected());
|
|
88
89
|
t.truthy(stash1.isHeld(weakStore.get(key1)));
|
|
89
90
|
|
|
90
91
|
// do the same, but exercise weakStore.set instead of .init
|
|
91
92
|
const key2 = makeKey();
|
|
92
93
|
const stash2 = stashRemotableTwo(weakStore, key2);
|
|
93
94
|
await gcAndFinalize();
|
|
94
|
-
t.
|
|
95
|
+
t.false(stash2.isCollected());
|
|
95
96
|
t.truthy(stash2.isHeld(weakStore.get(key2)));
|
|
96
97
|
|
|
97
98
|
// now stash a Remotable in the state of a virtual object during init()
|
|
@@ -100,12 +101,12 @@ test('remotables retained by virtualized data', async t => {
|
|
|
100
101
|
// Each state property is virtualized upon write (via the generated
|
|
101
102
|
// setters). So again we rely on the VOM to keep the Remotable alive in
|
|
102
103
|
// case someone retrieves it again.
|
|
103
|
-
t.
|
|
104
|
+
t.false(stash3.isCollected());
|
|
104
105
|
t.truthy(stash3.isHeld(stash3.holder.getHeld()));
|
|
105
106
|
|
|
106
107
|
// same, but stash after init()
|
|
107
108
|
const stash4 = stashRemotableFour(makeHolder);
|
|
108
109
|
await gcAndFinalize();
|
|
109
|
-
t.
|
|
110
|
+
t.false(stash4.isCollected());
|
|
110
111
|
t.truthy(stash4.isHeld(stash4.holder.getHeld()));
|
|
111
112
|
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
|
-
/* global WeakRef */
|
|
3
2
|
import test from 'ava';
|
|
4
3
|
|
|
5
4
|
import { Far } from '@endo/marshal';
|
|
6
5
|
import { kser, kunser } from '@agoric/kmarshal';
|
|
7
6
|
import { setupTestLiveslots } from '../liveslots-helpers.js';
|
|
7
|
+
import { watchCollected } from '../gc-and-finalize.js';
|
|
8
8
|
|
|
9
9
|
test('virtual object state writes', async t => {
|
|
10
|
-
let
|
|
10
|
+
let collected;
|
|
11
11
|
|
|
12
12
|
const initData = { begin: 'ning' };
|
|
13
13
|
const initStateData = { begin: kser(initData.begin) };
|
|
@@ -23,11 +23,11 @@ test('virtual object state writes', async t => {
|
|
|
23
23
|
const root = Far('root', {
|
|
24
24
|
make: () => {
|
|
25
25
|
const thing = makeThing();
|
|
26
|
-
|
|
26
|
+
collected = watchCollected(thing);
|
|
27
27
|
return thing;
|
|
28
28
|
},
|
|
29
29
|
ping: thing => {
|
|
30
|
-
|
|
30
|
+
collected = watchCollected(thing);
|
|
31
31
|
return thing.ping();
|
|
32
32
|
},
|
|
33
33
|
});
|
|
@@ -56,7 +56,7 @@ test('virtual object state writes', async t => {
|
|
|
56
56
|
|
|
57
57
|
// 'thing' is exported, but not held in RAM, so the Representative
|
|
58
58
|
// should be dropped
|
|
59
|
-
t.
|
|
59
|
+
t.true(collected.result);
|
|
60
60
|
|
|
61
61
|
// Invoking a method, on the other hand, *does* require creation of
|
|
62
62
|
// "state" and "context", and creation of "state" requires reading
|
|
@@ -70,5 +70,5 @@ test('virtual object state writes', async t => {
|
|
|
70
70
|
|
|
71
71
|
// 'thing' is again dropped by RAM, so it should be dropped. If
|
|
72
72
|
// "context" were erroneously retained, it would stick around.
|
|
73
|
-
t.
|
|
73
|
+
t.true(collected.result);
|
|
74
74
|
});
|