Skip to content

Commit 553a2a4

Browse files
committed
Use safe stringify from -gui for visual reports
Fixes stringifying -0, objects, arrays
1 parent 7fefa6f commit 553a2a4

File tree

4 files changed

+86
-1
lines changed

4 files changed

+86
-1
lines changed

src/engine/runtime.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const ScratchLinkWebSocket = require('../util/scratch-link-websocket');
2323
const FontManager = require('./tw-font-manager');
2424
const fetchWithTimeout = require('../util/fetch-with-timeout');
2525
const platform = require('./tw-platform.js');
26+
const safeStringify = require('../util/tw-safe-stringify.js');
2627

2728
// Virtual I/O devices.
2829
const Clock = require('../io/clock');
@@ -3084,7 +3085,10 @@ class Runtime extends EventEmitter {
30843085
*/
30853086
visualReport (target, blockId, value) {
30863087
if (target === this.getEditingTarget()) {
3087-
this.emit(Runtime.VISUAL_REPORT, {id: blockId, value: String(value)});
3088+
this.emit(Runtime.VISUAL_REPORT, {
3089+
id: blockId,
3090+
value: safeStringify(value)
3091+
});
30883092
}
30893093
}
30903094

src/util/tw-safe-stringify.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const circularReplacer = () => {
2+
const seen = new WeakSet();
3+
return (_, value) => {
4+
if (typeof value === 'object' && value !== null) {
5+
if (seen.has(value)) {
6+
return Array.isArray(value) ? '[...]' : '{...}';
7+
}
8+
seen.add(value);
9+
}
10+
return value;
11+
};
12+
};
13+
14+
/**
15+
* Safely stringify, properly handling circular relations and -0.
16+
* @param {unknown} input Any value
17+
* @returns {string} A stringified version of the input.
18+
*/
19+
const safeStringify = input => {
20+
if (typeof input === 'object' && input !== null) {
21+
return JSON.stringify(input, circularReplacer());
22+
}
23+
// -0 stringifies as "0" by default.
24+
if (Object.is(input, -0)) {
25+
return '-0';
26+
}
27+
return `${input}`;
28+
};
29+
30+
module.exports = safeStringify;

test/unit/engine_runtime_tw.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const tap = require('tap');
22
const Runtime = require('../../src/engine/runtime');
33
const {Map} = require('immutable');
44
const makeTestStorage = require('../fixtures/make-test-storage');
5+
const Target = require('../../src/engine/target');
56

67
const test = tap.test;
78

@@ -253,3 +254,21 @@ test('convertToPackagedRuntime and attachStorage call order', t => {
253254
rt2.attachStorage(makeTestStorage());
254255
t.end();
255256
});
257+
258+
test('visual report -0', t => {
259+
t.plan(1);
260+
261+
const rt = new Runtime();
262+
const target = new Target();
263+
rt.setEditingTarget(target);
264+
265+
rt.on(Runtime.VISUAL_REPORT, report => {
266+
t.same(report, {
267+
id: 'blockid',
268+
value: '-0'
269+
});
270+
});
271+
rt.visualReport(target, 'blockid', -0);
272+
273+
t.end();
274+
});

test/unit/tw_safe_stringify.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const {test} = require('tap');
2+
const safeStringify = require('../../src/util/tw-safe-stringify');
3+
4+
test('safeStringify', t => {
5+
t.equal(safeStringify(''), '');
6+
t.equal(safeStringify('a'), 'a');
7+
8+
t.equal(safeStringify(true), 'true');
9+
t.equal(safeStringify(false), 'false');
10+
11+
t.equal(safeStringify(0), '0');
12+
t.equal(safeStringify(-0), '-0');
13+
t.equal(safeStringify(1), '1');
14+
t.equal(safeStringify(Infinity), 'Infinity');
15+
t.equal(safeStringify(-Infinity), '-Infinity');
16+
t.equal(safeStringify(NaN), 'NaN');
17+
18+
t.equal(safeStringify(null), 'null');
19+
t.equal(safeStringify(undefined), 'undefined');
20+
21+
t.equal(safeStringify({}), '{}');
22+
t.equal(safeStringify({a: 'b'}), '{"a":"b"}');
23+
24+
t.equal(safeStringify([]), '[]');
25+
t.equal(safeStringify([1, 2, 3]), '[1,2,3]');
26+
27+
const circular = {};
28+
circular.circular = circular;
29+
t.equal(safeStringify(circular), '{"circular":"{...}"}');
30+
31+
t.end();
32+
});

0 commit comments

Comments
 (0)