値ブロックの引数のキャッシュ

ブロックコード ジェネレータでは、内部ブロックのコードを複数回参照しなければならない場合があります。

たとえば、リストの最後の要素を取得するブロックがある場合、リストコードに複数回アクセスする必要があります。

// 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];
}

ただし、内部ブロックのコードの結果の値が一致しない場合や、副作用が生じる場合は、問題が発生する可能性があります。たとえば、内部コードが実際には関数呼び出しである場合、次の特定のコードは範囲外の状態になる可能性があります。

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

ユーティリティ関数を使用すると、内部ブロックのコードを 1 回だけ評価できます。

ユーティリティ関数

ユーティリティ関数は、生成されたコード文字列の一部として含まれる、デベロッパー定義の関数です。これを使用して、内部ブロックのコードが 1 回だけ評価され、その後その値が複数回参照されるようにできます。

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];
}

関数を指定する

provideFunction_ を使用して、ブロックコード ジェネレータ内でユーティリティ関数を定義できます。この関数は、ユーティリティ関数に付ける名前と、関数の機能を定義するコード文字列の配列を受け取ります。ユーザー定義関数と競合しないように(場合によっては)ユーティリティ関数を変更した後、その結果としてそのユーティリティ関数の名前が返されます。

また、provideFunction_ はユーティリティ関数の定義を重複させるので、各ユーティリティ関数は、それを定義するブロックタイプが複数回存在しても、存在するのは 1 回だけです。

優先度を更新する

ユーティリティ関数を定義するときは、ブロックコード ジェネレータに含まれる優先順位(かっこの挿入方法を定義する)も更新する必要があります。

優先度は常に、ブロックコード ジェネレータが返すコード文字列に基づきます。ユーティリティ関数内の演算子は考慮されません。そのため、前の例では、valueToCode 呼び出しが Order.NONE に変更され、戻りタプルが Order.FUNCTION_CALL に変更されました。