ステートメント ブロックによる引数のキャッシュ保存

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

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

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

  // listCode gets referenced twice.
  return `print(${listCode}[${listCode}.length - 1]);\n`;
}

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

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

一時変数に代入すると、内部ブロックのコードを 1 回だけ評価できます。

一時変数

一時変数には内部ブロックのコード文字列の値が格納されます。コードが評価されるのは 1 回だけで、その後その値は複数回参照できます。

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

// Correct block-code generator.
javascriptGenerator.forBlock['print_last_element'] = function(block, generator) {
  const listCode = generator.valueToCode(block, 'LIST', Order.MEMBER);
  const listVar = generator.nameDB_.getDistinctName(
      'temp_list', Blockly.names.NameType.VARIABLE);

  // listCode only gets evaluated once.
  const code = `var ${listVar} = ${listCode};\n`;
  return `print(${listVar}[${listVar}.length - 1]);\n`;
}

getDistinctName 呼び出しは、必要な変数名を受け取り、ユーザー定義変数と競合しない名前を返します。

冗長なコードの削減

一時変数の欠点は、内部ブロックのコードが関数や式ではなく値の場合、コードが冗長になることです。

// Assigning to temp_list is unnecessary.
var temp_list = foo;
print(temp_list[temp_list.length - 1]);

すっきりしたコードを生成するには、内部ブロックのコードが値かどうかを確認し、値でない場合は一時変数のみを含めます。

if (listCode.match(/^\w+$/)) {
  const code = `print(${listCode}[${listCode}.length - 1]);\n`;
} else {
  const listVar = generator.nameDB_.getDistinctName(
      'temp_list', Blockly.names.NameType.VARIABLE);
  const code = `var ${listVar} = ${listCode};\n`;
  code += `print(${listVar}[${listVar}.length - 1]);\n`;
}