Value block argument caching

Sometimes your block-code generator needs to reference the code of its inner block multiple times.

For example, if you have a block that gets the last element of a list, you need to access the list code multiple times:

// Incorrect block-code generator.
javascriptGenerator.forBlock['last_element'] = function(block, generator) {
  const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);

  // listCode gets referenced twice.
  const code = `${listCode}[${listCode}.length - 1]`;

  return [code, Order.MEMBER];
}

But this can cause problems if the resulting value of the inner block's code is inconsistent, or it has side effects. For example, if the inner code is actually a function call, this particular code can end up with an out of range condition:

randomList()[randomList().length - 1]

Using utility functions lets you make sure that inner blocks' code is only evaluated once.

Utility functions

A utility function is a developer-defined function included as part of the generated code string. You can use them to make sure that inner-block code is only evaluated once, and then the value can be referenced multiple times.

import {javascriptGenerator, Order} from 'blockly/javascript';

// Correct block-code generator.
javascriptGenerator.forBlock['last_element'] = function(block, generator) {
  const listCode = generator.valueToCode(block, 'LIST', Order.NONE);
  const functionName = generator.provideFunction_(
      'list_lastElement',
      [
        `function ${generator.FUNCTION_NAME_PLACEHOLDER_}(list) {`,
        `  return list[list.length - 1];`,
        `}`
      ]
  );

  // listCode only gets evaluated once.
  const code = `${functionName}(${listCode})`;
  return [code, Order.FUNCTION_CALL];
}

Provide the function

You can define utility functions inside block-code generators using provideFunction_. It takes in the name you want for your utility function, and an array of code strings defining what the function does. It returns the resulting name of your utility function, after (possibly) modifying it to not conflict with user-defined functions.

provideFunction_ also dedupes utility function definitions, so that each utility function only exists once, even if the block type that defines it exists multiple times.

Update precedences

When you define a utility function, you should also update the precedences (which define how parentheses are inserted) included in the block-code generator.

The precedence is always based on the code string returned by the block-code generator. It does not care about operators inside utility functions. So in the previous example the valueToCode call was changed to Order.NONE and the return tuple was changed to Order.FUNCTION_CALL.