From aa16930e34be826c63dbac651dbaa435579fbe2d Mon Sep 17 00:00:00 2001 From: Connor Hudson Date: Mon, 18 Jun 2018 15:00:33 -0400 Subject: [PATCH 1/5] Add initial implementation of extension serialization/deserialization --- src/serialization/sb3.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 4f129c171..a47e61d90 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -272,14 +272,20 @@ const compressInputTree = function (block, blocks) { * Serialize the given blocks object (representing all the blocks for the target * currently being serialized.) * @param {object} blocks The blocks to be serialized + * @param {Set} extensionIDs Set of extension ids * @return {object} The serialized blocks with compressed inputs and compressed * primitives. */ -const serializeBlocks = function (blocks) { +const serializeBlocks = function (blocks, extensionIDs) { const obj = Object.create(null); for (const blockID in blocks) { if (!blocks.hasOwnProperty(blockID)) continue; obj[blockID] = serializeBlock(blocks[blockID], blocks); + const index = blocks[blockID].opcode.indexOf('_'); + const prefix = blocks[blockID].opcode.substring(0, index); + if (CORE_EXTENSIONS.indexOf(prefix) === -1) { + if (prefix !== '') extensionIDs.add(prefix); + } } // once we have completed a first pass, do a second pass on block inputs for (const blockID in obj) { @@ -416,13 +422,15 @@ const serializeComments = function (comments) { */ const serializeTarget = function (target) { const obj = Object.create(null); + const extensionIDs = new Set(); obj.isStage = target.isStage; obj.name = obj.isStage ? 'Stage' : target.name; const vars = serializeVariables(target.variables); obj.variables = vars.variables; obj.lists = vars.lists; obj.broadcasts = vars.broadcasts; - obj.blocks = serializeBlocks(target.blocks); + obj.blocks = serializeBlocks(target.blocks, extensionIDs); + obj.extensions = Array.from(extensionIDs); obj.comments = serializeComments(target.comments); obj.currentCostume = target.currentCostume; obj.costumes = target.costumes.map(serializeCostume); @@ -901,6 +909,9 @@ const parseScratchObject = function (object, runtime, extensions, zip) { if (object.hasOwnProperty('isStage')) { target.isStage = object.isStage; } + if (object.hasOwnProperty('extensions')) { + target.extensions = object.extensions; + } Promise.all(costumePromises).then(costumes => { sprite.costumes = costumes; }); From 15de2eaa686198ca35ae87937446c202b6d7afe6 Mon Sep 17 00:00:00 2001 From: Connor Hudson Date: Wed, 20 Jun 2018 14:33:01 -0400 Subject: [PATCH 2/5] Remove extension deserialization --- src/serialization/sb3.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index a47e61d90..a1c42651d 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -909,9 +909,6 @@ const parseScratchObject = function (object, runtime, extensions, zip) { if (object.hasOwnProperty('isStage')) { target.isStage = object.isStage; } - if (object.hasOwnProperty('extensions')) { - target.extensions = object.extensions; - } Promise.all(costumePromises).then(costumes => { sprite.costumes = costumes; }); From 17679340f976235263ca3c495013d06d8edf1944 Mon Sep 17 00:00:00 2001 From: Connor Hudson Date: Wed, 20 Jun 2018 15:42:56 -0400 Subject: [PATCH 3/5] Return extension ids from serializeBlocks instead of modifying Set passed to it --- src/serialization/sb3.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index a1c42651d..bbfbf4098 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -272,12 +272,13 @@ const compressInputTree = function (block, blocks) { * Serialize the given blocks object (representing all the blocks for the target * currently being serialized.) * @param {object} blocks The blocks to be serialized - * @param {Set} extensionIDs Set of extension ids - * @return {object} The serialized blocks with compressed inputs and compressed - * primitives. + * @return {Array} An array of the serialized blocks with compressed inputs and + * compressed primitives and the list of all extension IDs present + * in the serialized blocks. */ -const serializeBlocks = function (blocks, extensionIDs) { +const serializeBlocks = function (blocks) { const obj = Object.create(null); + const extensionIDs = new Set(); for (const blockID in blocks) { if (!blocks.hasOwnProperty(blockID)) continue; obj[blockID] = serializeBlock(blocks[blockID], blocks); @@ -314,7 +315,7 @@ const serializeBlocks = function (blocks, extensionIDs) { delete obj[blockID]; } } - return obj; + return [obj, Array.from(extensionIDs)]; }; /** @@ -422,15 +423,13 @@ const serializeComments = function (comments) { */ const serializeTarget = function (target) { const obj = Object.create(null); - const extensionIDs = new Set(); obj.isStage = target.isStage; obj.name = obj.isStage ? 'Stage' : target.name; const vars = serializeVariables(target.variables); obj.variables = vars.variables; obj.lists = vars.lists; obj.broadcasts = vars.broadcasts; - obj.blocks = serializeBlocks(target.blocks, extensionIDs); - obj.extensions = Array.from(extensionIDs); + [obj.blocks, obj.extensions] = serializeBlocks(target.blocks); obj.comments = serializeComments(target.comments); obj.currentCostume = target.currentCostume; obj.costumes = target.costumes.map(serializeCostume); From 026dd96491f3588012a6c0d1db3647c72195810b Mon Sep 17 00:00:00 2001 From: Connor Hudson Date: Thu, 21 Jun 2018 15:00:17 -0400 Subject: [PATCH 4/5] Modify serializeTarget function to take an extension set as an argument This allows the serialize function to build a list of all extensions used --- src/serialization/sb3.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index bbfbf4098..1b5c56336 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -419,17 +419,19 @@ const serializeComments = function (comments) { * Serialize the given target. Only serialize properties that are necessary * for saving and loading this target. * @param {object} target The target to be serialized. + * @param {Set} extensions A set of extensions to add extension IDs to * @return {object} A serialized representation of the given target. */ -const serializeTarget = function (target) { +const serializeTarget = function (target, extensions) { const obj = Object.create(null); + let targetExtensions = []; obj.isStage = target.isStage; obj.name = obj.isStage ? 'Stage' : target.name; const vars = serializeVariables(target.variables); obj.variables = vars.variables; obj.lists = vars.lists; obj.broadcasts = vars.broadcasts; - [obj.blocks, obj.extensions] = serializeBlocks(target.blocks); + [obj.blocks, targetExtensions] = serializeBlocks(target.blocks); obj.comments = serializeComments(target.comments); obj.currentCostume = target.currentCostume; obj.costumes = target.costumes.map(serializeCostume); @@ -448,6 +450,11 @@ const serializeTarget = function (target) { obj.draggable = target.draggable; obj.rotationStyle = target.rotationStyle; } + + // Add found extensions to the extensions object + targetExtensions.forEach(extensionId => { + extensions.add(extensionId); + }); return obj; }; From befcbec3937c00a69b524ed3dff152f04beade6c Mon Sep 17 00:00:00 2001 From: Connor Hudson Date: Thu, 21 Jun 2018 15:01:00 -0400 Subject: [PATCH 5/5] Create extensions list in serialize function Removed runtime argument from serializeTarget call because it wasn't used --- src/serialization/sb3.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/serialization/sb3.js b/src/serialization/sb3.js index 1b5c56336..99c6ec226 100644 --- a/src/serialization/sb3.js +++ b/src/serialization/sb3.js @@ -466,12 +466,17 @@ const serializeTarget = function (target, extensions) { const serialize = function (runtime) { // Fetch targets const obj = Object.create(null); + // Create extension set to hold extension ids found while serializing targets + const extensions = new Set(); const flattenedOriginalTargets = JSON.parse(JSON.stringify( runtime.targets.filter(target => target.isOriginal))); - obj.targets = flattenedOriginalTargets.map(t => serializeTarget(t, runtime)); + obj.targets = flattenedOriginalTargets.map(t => serializeTarget(t, extensions)); // TODO Serialize monitors + // Assemble extension list + obj.extensions = Array.from(extensions); + // Assemble metadata const meta = Object.create(null); meta.semver = '3.0.0';