Udon の実行モデル
Udon の命令は 9 種類です。命令が指すスロットは、値の置き場のことです(値の置き場)。
| 命令 | 何をするか |
|---|---|
NOP | 何もしない |
PUSH | スロットの番号をスタックに積む |
POP | スタックの先頭を捨てる |
JUMP_IF_FALSE | 積んだ番号のスロットが false なら、指定の位置へ跳ぶ |
JUMP | 指定の位置へ跳ぶ |
EXTERN | 外部の関数を 1 本呼ぶ |
ANNOTATION | 何もしない(引数を 1 つ取る) |
JUMP_INDIRECT | スロットに入っている値を飛び先にして跳ぶ |
COPY | スタックが指す 2 つのスロットのあいだで値を写す |
値を計算するのは EXTERN だけです。a + b も、実行環境が持っている足し算の関数を名前で
呼ぶ形になります。COPY はスロットの間で値を写し、残りの命令は番号を積むか跳ぶかをします。
呼べる関数は、実行環境が公開している集合に限られます。C# の側に存在するメソッドでも、
この集合に無ければ TUKI0101 で断られます。型も同じで、集合に無い型は TUKI0102 です
(エラー)。
呼ぶ関数の名前は、シグネチャの文字列として命令列に書き込まれます。飛び先を実行時に決められる
命令は JUMP_INDIRECT ただ 1 つで、コンパイラはこれをメソッドからの復帰に使います。
この形から、ほかのページに出てくる「書けない形」のうち大きいものが 3 つ出ます。
| 書けない形 | 理由 | 出る番号 |
|---|---|---|
| 関数を値として持つ形 | 呼ぶ先は命令の側に書いてあり、実行時に差し替えられません | デリゲートとラムダが TUKI0108 |
| 中断して再開する形 | 実行はエントリポイントから終わりまで通しで進みます | yield と await が TUKI0001 |
| 実行時に名前でメンバを探す形 | 呼ぶ先の名前はコンパイルの時に決まります | dynamic が TUKI0102 |
実行環境に局所変数の枠はありません。値はすべて、ヒープと呼ぶ 1 本の配列のスロットに載ります。 スロットは 1 つずつ型を持ち、その数はコンパイルの時に決まります。実行中に増やす手段はありません。
配列は、要素をいくつ持っていても 1 つのスロットに載ります。長さは実行時に決めた値で かまいませんが、作ったあとで長さを変える書き方はありません(配列)。
プログラムの単位
Section titled “プログラムの単位”1 つのファイルが 1 つのプログラムになり、その中の具象の Behaviour がプログラムの本体になります。
2 つ目の具象の Behaviour を同じファイルに書くと TUKI0109 で断られます
(プログラムの形)。
プログラムに main はありません。実行が始まる場所は、実行環境が呼ぶイベントと、
名前で呼ばれるメソッドです(イベント)。Main という名前のメソッドは
書けますが、実行環境はそれを呼びません。
using UnityEngine;using Tsukimi;
public class BcMain : TsukimiBehaviour{ void Start() { Debug.Log(1); // => 1 }
public static void Main() { }}エントリポイントからたどれないコード
Section titled “エントリポイントからたどれないコード”コンパイラはエントリポイントから呼び出しをたどり、届いたメソッドだけを命令にします。エントリポイントは、イベントと
public を付けたメソッドです。public の付いていないメソッドで、どこからも呼ばれない
ものは、命令になりません。スロットも使いません。
命令にならないメソッドも、書ける形かどうかの検査は受けます。呼び忘れたメソッドの中に 書けない形があれば、そこで断られます(境界の一覧)。
スロットの上限
Section titled “スロットの上限”1 つのプログラムが使えるスロットの数には上限があります。コンパイラは、struct の中身まで
展開したフィールドと、各メソッドの引数と局所変数を数え、512 を超えると TUKI0104 で止めます。
struct を小さくするか、変数を減らすと通ります。この数を数えるのは struct を使った
プログラムだけで、struct の無いプログラムでは数えません。
スロットを使うのは、ソースに書いた変数だけではありません。
| 何が使うか | 数え方 |
|---|---|
| フィールド | 宣言した数だけ使います。struct は中身を並べた形になるので、メンバの数だけ使います |
| メソッドの引数と局所変数 | 実行環境に局所変数の枠が無いので、メソッドの中で宣言した変数もここに載ります |
| 定数と計算の途中の値 | ソースに書いていない値も、コンパイルの過程で置き場が要ります |
| 外部の関数のシグネチャ文字列 | 呼ぶ関数の種類ごとに 1 つ使います。同じ関数を何度呼んでも増えません |
呼ぶ関数の種類が増えると、変数に使える枠はそのぶん減ります。
TUKI0104 が数えているのは、フィールドと、メソッドの引数と局所変数です。組み立てのときに
実際に要る量には、そこへ定数と計算の途中の値、それにシグネチャの文字列が加わります。
この数は見積もりで、実際の使用量とは違います。
512 は実行環境の壁ではありません。Udon のプログラムを組み立てるアセンブラが、既定でこの 大きさのヒープを用意する、というだけの数字です。大きさを指定して組み立てることもでき、 512 を超えるヒープを持つプログラムが実際に動いています。
ジェネリクスには別の上限が 2 つあります。具体化した組の総数が 256 を超えるか、型引数の入れ子が
8 段より深くなると TUKI0105 で止まります。どちらもコンパイラが増殖を防ぐために置いている
数で、実行環境の側の制限ではありません。この 2 つの検査は TUKI0104 より先に走ります。
実行が止まる条件
Section titled “実行が止まる条件”実行環境に例外の仕組みはありません。throw も try / catch も断られます
(境界の一覧)。
using UnityEngine;using Tsukimi;
public class ExCatchTypeless : TsukimiBehaviour{ void Start() { try { Debug.Log(1); } catch { Debug.Log(2); } }}失敗しうる操作は、呼ぶ前に条件を調べる形でしか書けません。失敗してから拾い直す書き方は、
この環境では成り立ちません。checked があふれを見つけないのも同じ理由です(数値)。
| 操作 | 起きること | 例のある場所 |
|---|---|---|
| 整数のゼロ除算 | 実行が止まります。小数を 0 で割ったときは Infinity になります | 数値 |
| 範囲の外へ出る数値の変換 | 実行が止まります。変換する外部の関数が範囲を検査しています | 数値 |
| null の参照のメンバの呼び出し | 実行が止まり、実行環境がエラーを出します | null と参照 |
止まると、そのイベントの残りは走りません。ログには、止まったことと、失敗した外部の関数の シグネチャが出ます。そのプログラムが以後どのイベントにも反応しなくなることがあり、 そうなったときに戻す手段はありません。
止まったあとにプログラムを戻す手段はありません。作り直すか、置き直すことになります。