mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-23 14:32:59 -05:00
Serialize Infinity and NaN to 0
This commit is contained in:
parent
122443a75f
commit
7e96ef2985
5 changed files with 64 additions and 4 deletions
|
@ -14,6 +14,7 @@ const StageLayering = require('../engine/stage-layering');
|
||||||
const log = require('../util/log');
|
const log = require('../util/log');
|
||||||
const uid = require('../util/uid');
|
const uid = require('../util/uid');
|
||||||
const MathUtil = require('../util/math-util');
|
const MathUtil = require('../util/math-util');
|
||||||
|
const StringUtil = require('../util/string-util');
|
||||||
|
|
||||||
const {loadCostume} = require('../import/load-costume.js');
|
const {loadCostume} = require('../import/load-costume.js');
|
||||||
const {loadSound} = require('../import/load-sound.js');
|
const {loadSound} = require('../import/load-sound.js');
|
||||||
|
@ -522,7 +523,7 @@ const serialize = function (runtime, targetId) {
|
||||||
|
|
||||||
const layerOrdering = getSimplifiedLayerOrdering(originalTargetsToSerialize);
|
const layerOrdering = getSimplifiedLayerOrdering(originalTargetsToSerialize);
|
||||||
|
|
||||||
const flattenedOriginalTargets = JSON.parse(JSON.stringify(originalTargetsToSerialize));
|
const flattenedOriginalTargets = JSON.parse(StringUtil.stringify(originalTargetsToSerialize));
|
||||||
|
|
||||||
// If the renderer is attached, and we're serializing a whole project (not a sprite)
|
// If the renderer is attached, and we're serializing a whole project (not a sprite)
|
||||||
// add a temporary layerOrder property to each target.
|
// add a temporary layerOrder property to each target.
|
||||||
|
|
|
@ -36,6 +36,27 @@ class StringUtil {
|
||||||
return [text, null];
|
return [text, null];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A customized version of JSON.stringify that sets Infinity/NaN to 0,
|
||||||
|
* instead of the default (null).
|
||||||
|
* Needed because null is not of type number, but Infinity/NaN are, which
|
||||||
|
* can lead to serialization producing JSON that isn't valid based on the parser schema.
|
||||||
|
* It is also consistent with the behavior of saving 2.0 projects.
|
||||||
|
* This is only needed when stringifying an object for saving.
|
||||||
|
*
|
||||||
|
* @param {!object} obj - The object to serialize
|
||||||
|
* @return {!string} The JSON.stringified string with Infinity/NaN replaced with 0
|
||||||
|
*/
|
||||||
|
static stringify (obj) {
|
||||||
|
return JSON.stringify(obj, (_key, value) => {
|
||||||
|
if (typeof value === 'number' &&
|
||||||
|
(value === Infinity || value === -Infinity || isNaN(value))){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = StringUtil;
|
module.exports = StringUtil;
|
||||||
|
|
|
@ -381,7 +381,7 @@ class VirtualMachine extends EventEmitter {
|
||||||
exportSprite (targetId, optZipType) {
|
exportSprite (targetId, optZipType) {
|
||||||
const soundDescs = serializeSounds(this.runtime, targetId);
|
const soundDescs = serializeSounds(this.runtime, targetId);
|
||||||
const costumeDescs = serializeCostumes(this.runtime, targetId);
|
const costumeDescs = serializeCostumes(this.runtime, targetId);
|
||||||
const spriteJson = JSON.stringify(sb3.serialize(this.runtime, targetId));
|
const spriteJson = StringUtil.stringify(sb3.serialize(this.runtime, targetId));
|
||||||
|
|
||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
zip.file('sprite.json', spriteJson);
|
zip.file('sprite.json', spriteJson);
|
||||||
|
@ -401,7 +401,7 @@ class VirtualMachine extends EventEmitter {
|
||||||
* @return {string} Serialized state of the runtime.
|
* @return {string} Serialized state of the runtime.
|
||||||
*/
|
*/
|
||||||
toJSON () {
|
toJSON () {
|
||||||
return JSON.stringify(sb3.serialize(this.runtime));
|
return StringUtil.stringify(sb3.serialize(this.runtime));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO do we still need this function? Keeping it here so as not to introduce
|
// TODO do we still need this function? Keeping it here so as not to introduce
|
||||||
|
|
|
@ -63,3 +63,14 @@ test('unusedName', t => {
|
||||||
);
|
);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('stringify', t => {
|
||||||
|
const obj = {a: Infinity, b: NaN, c: -Infinity, d: 23, e: 'str'};
|
||||||
|
const parsed = JSON.parse(StringUtil.stringify(obj));
|
||||||
|
t.equal(parsed.a, 0);
|
||||||
|
t.equal(parsed.b, 0);
|
||||||
|
t.equal(parsed.c, 0);
|
||||||
|
t.equal(parsed.d, 23);
|
||||||
|
t.equal(parsed.e, 'str');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
|
@ -982,3 +982,30 @@ test('Starting the VM emits an event', t => {
|
||||||
t.equal(started, true);
|
t.equal(started, true);
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('toJSON encodes Infinity/NaN as 0, not null', t => {
|
||||||
|
const vm = new VirtualMachine();
|
||||||
|
const runtime = vm.runtime;
|
||||||
|
const spr1 = new Sprite(null, runtime);
|
||||||
|
const stage = spr1.createClone();
|
||||||
|
stage.isStage = true;
|
||||||
|
stage.volume = Infinity;
|
||||||
|
stage.tempo = NaN;
|
||||||
|
stage.createVariable('id1', 'name1', '');
|
||||||
|
stage.variables.id1.value = Infinity;
|
||||||
|
stage.createVariable('id2', 'name2', '');
|
||||||
|
stage.variables.id1.value = -Infinity;
|
||||||
|
stage.createVariable('id3', 'name3', '');
|
||||||
|
stage.variables.id1.value = NaN;
|
||||||
|
|
||||||
|
runtime.targets = [stage];
|
||||||
|
|
||||||
|
const json = JSON.parse(vm.toJSON());
|
||||||
|
t.equal(json.targets[0].volume, 0);
|
||||||
|
t.equal(json.targets[0].tempo, 0);
|
||||||
|
t.equal(json.targets[0].variables.id1[1], 0);
|
||||||
|
t.equal(json.targets[0].variables.id2[1], 0);
|
||||||
|
t.equal(json.targets[0].variables.id3[1], 0);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in a new issue