@@ -16,6 +16,8 @@ const uid = require('../util/uid');
1616const MathUtil = require ( '../util/math-util' ) ;
1717const StringUtil = require ( '../util/string-util' ) ;
1818const VariableUtil = require ( '../util/variable-util' ) ;
19+ const mutationAdapter = require ( '../engine/mutation-adapter' ) ;
20+ const xmlEscape = require ( '../util/xml-escape' ) ;
1921
2022const { loadCostume} = require ( '../import/load-costume.js' ) ;
2123const { loadSound} = require ( '../import/load-sound.js' ) ;
@@ -852,6 +854,16 @@ const deserializeFields = function (fields) {
852854 return obj ;
853855} ;
854856
857+ /**
858+ * Convert dynamic blockInfo JSON from a custom deserialization function into an XML element ready for runtime use.
859+ * @param {object } dynamicBlockInfo - a BlockInfo-like object with properties like `opcode` and `isDynamic`.
860+ * @returns {Element } - an XML Element with tag name 'mutation' and an attribute called 'blockInfo'
861+ */
862+ const makeMutation = function ( dynamicBlockInfo ) {
863+ const xmlString = `<mutation blockInfo="${ xmlEscape ( JSON . stringify ( dynamicBlockInfo ) ) } "/>` ;
864+ return mutationAdapter ( xmlString ) ;
865+ } ;
866+
855867/**
856868 * Convert serialized INPUT and FIELD primitives back to hydrated block templates.
857869 * Should be able to deserialize a format that has already been deserialized. The only
@@ -867,26 +879,34 @@ const deserializeBlocks = function (runtime, blocks) {
867879 if ( ! blocks . hasOwnProperty ( blockId ) ) {
868880 continue ;
869881 }
870- const block = blocks [ blockId ] ;
882+ let block = blocks [ blockId ] ;
871883 const { extensionId, extendedOpcode} = getExtensionAndOpcode ( block ) ;
872884 const extensionInfo = runtime . getExtensionInfo ( extensionId ) ;
873885 const customDeserialize = getSerializationInfo ( extensionInfo , extendedOpcode ) . deserialize ;
874886 if ( customDeserialize ) {
875- const newBlock = customDeserialize ( block ) ;
876- newBlock . id = uid ( ) ;
877- delete blocks [ blockId ] ;
878- blocks [ newBlock . id ] = newBlock ;
879- // fall through to input/field handling
880- }
881- if ( Array . isArray ( block ) ) {
887+ block = customDeserialize ( block ) ;
888+ blocks [ blockId ] = block ; // customDeserialize might have made a new object; make sure to keep it!
889+ // the deserialize function should return opcode 'foo' and we'll fix it to 'ext_foo'
890+ const newBlockOpcode = `${ extensionId } _${ block . opcode } ` ;
891+ if ( newBlockOpcode !== extendedOpcode ) {
892+ log . warn ( `Block deserialization changed opcode from ${ extendedOpcode } to ${ block . opcode } ` ) ;
893+ }
894+ block . opcode = newBlockOpcode ;
895+ if ( block . mutation && block . mutation . blockInfo ) {
896+ block . mutation = makeMutation ( block . mutation . blockInfo ) ;
897+ }
898+ // fall through to input & field handling below
899+ } else if ( Array . isArray ( block ) ) {
882900 // this is a compressed primitive expressed as an array instead of as an object
883901 // delete the old entry in object.blocks and replace it w/the deserialized object
884902 delete blocks [ blockId ] ;
885903 deserializeInputDesc ( block , null , false , blocks ) ;
904+ // deserializeInputDesc did any id/input/field handling needed, so just skip to the next block
886905 continue ;
887- } else {
888- block . id = blockId ; // add id back to block since it wasn't serialized
889906 }
907+ // we want this stuff to run for custom-deserialized blocks as well as for default-deserialized blocks
908+ // except for primitives, which are handled specially inside `deserializeInputDesc` above.
909+ block . id = blockId ; // add id back to block since it wasn't serialized
890910 block . inputs = deserializeInputs ( block . inputs , blockId , blocks ) ;
891911 block . fields = deserializeFields ( block . fields ) ;
892912 }
0 commit comments