From 833d33355c07897cd9b22e96a76e281b71be36f3 Mon Sep 17 00:00:00 2001
From: Christopher Willis-Ford <cwillisf@media.mit.edu>
Date: Fri, 19 Apr 2019 12:28:19 -0700
Subject: [PATCH] retrieve blockInfo from args when isDynamic is set

---
 src/engine/mutation-adapter.js             |  4 ++
 src/extension-support/extension-manager.js | 46 ++++++++++++++--------
 2 files changed, 34 insertions(+), 16 deletions(-)

diff --git a/src/engine/mutation-adapter.js b/src/engine/mutation-adapter.js
index d1984d30e..8cfac7634 100644
--- a/src/engine/mutation-adapter.js
+++ b/src/engine/mutation-adapter.js
@@ -13,6 +13,10 @@ const mutatorTagToObject = function (dom) {
     for (const prop in dom.attribs) {
         if (prop === 'xmlns') continue;
         obj[prop] = decodeHtml(dom.attribs[prop]);
+        if (prop === 'blockinfo') {
+            obj.blockInfo = JSON.parse(obj.blockinfo);
+            delete obj.blockinfo;
+        }
     }
     for (let i = 0; i < dom.children.length; i++) {
         obj.children.push(
diff --git a/src/extension-support/extension-manager.js b/src/extension-support/extension-manager.js
index e70929b63..a2f89aae0 100644
--- a/src/extension-support/extension-manager.js
+++ b/src/extension-support/extension-manager.js
@@ -390,26 +390,40 @@ class ExtensionManager {
                 log.warn(`Ignoring opcode "${blockInfo.opcode}" for button with text: ${blockInfo.text}`);
             }
             break;
-        default:
-            if (blockInfo.opcode) {
-                const funcName = blockInfo.func ? this._sanitizeID(blockInfo.func) : blockInfo.opcode;
-
-                // Avoid promise latency unless necessary
-                if (dispatch._isRemoteService(serviceName)) {
-                    blockInfo.func = (args, util) => dispatch.call(serviceName, funcName, args, util, blockInfo);
-                } else {
-                    const serviceObject = dispatch.services[serviceName];
-                    if (!serviceObject[funcName]) {
-                        // The function might show up later as a dynamic property of the service object
-                        log.warn(`Could not find extension block function called ${funcName}`);
-                    }
-                    blockInfo.func = (args, util) => serviceObject[funcName](args, util, blockInfo);
-                }
-            } else {
+        default: {
+            if (!blockInfo.opcode) {
                 throw new Error('Missing opcode for block');
             }
+
+            const funcName = blockInfo.func ? this._sanitizeID(blockInfo.func) : blockInfo.opcode;
+
+            const getBlockInfo = blockInfo.isDynamic ?
+                args => args && args.mutation && args.mutation.blockInfo :
+                () => blockInfo;
+            const callBlockFunc = (() => {
+                if (dispatch._isRemoteService(serviceName)) {
+                    return (args, util, realBlockInfo) =>
+                        dispatch.call(serviceName, funcName, args, util, realBlockInfo);
+                }
+
+                // avoid promise latency if we can call direct
+                const serviceObject = dispatch.services[serviceName];
+                if (!serviceObject[funcName]) {
+                    // The function might show up later as a dynamic property of the service object
+                    log.warn(`Could not find extension block function called ${funcName}`);
+                }
+                return (args, util, realBlockInfo) =>
+                    serviceObject[funcName](args, util, realBlockInfo);
+            })();
+
+            blockInfo.func = (args, util) => {
+                const realBlockInfo = getBlockInfo(args);
+                // TODO: filter args using the keys of realBlockInfo.arguments? maybe only if sandboxed?
+                return callBlockFunc(args, util, realBlockInfo);
+            };
             break;
         }
+        }
 
         return blockInfo;
     }