アウトライン
コール情報保存機能の呼び出しチェーンの実行中にローカル変数や他のすべての機能の呼び出しスタック。ベリーは、コールスタックを呼び出し、スタックではなくCコールスタックよりも、スクリプトに特異的に指します。
be_vm.hであなたがフィールドVM構造と関連コールスタックで見ることができます。
struct bvm {
// ...
bvalue *stack; /* stack space */
bvalue *stacktop; /* stack top register */
bstack callstack; /* function call stack */
// ...
};
stack
そして、stacktop
ローカルストア・スタック変数を維持するため(以下「可変スタック」と呼ばれる、スタック空間の関数を指すvm.stack
使用される関数空間の期間)、およびcallstack
スタックのスタックフレームの関数です。
私たちはフィールドの作用を説明するために簡単なスクリプトを使用します。
def func1(c)
return c + 1
end
def func2(b)
return func1(b) + 2
end
def func3(a)
return func2(a) + 3
end
我々は実行するとfunc3(10)
、時間を実行するためのfunc1
内部コールチェーンのアップ:
call stack top
+-------------------------+
| function: func1 |
| local variable(s): c |
+-------------------------+
| function: func2 |
| local variable(s): b |
+-------------------------+
| function: func3 |
| local variable(s): a |
+-------------------------+
call stack base
明らかに、関数呼び出しチェーン上のすべてのローカル変数は、呼び出し元の関数が呼び出される関数の後に返却し続けることができることを保証するために格納する必要があります。関数呼び出しチェーン上のすべてのローカル変数は、呼び出し元の順番に配置され、これらの変数の値がに保存されますvm.stack
。だから、vm.stack
あるbvalue
配列。コールが最も深い場合チェーンに到達したとき、の変数vm.stack
配列は次の通りであります:
stack index | 0 | 1 | 2 |
variable | func3:a | func2:b | func1:c |
ここでは、それは注意する必要がありますvm.stack
ので、各機能がどのように判断するために、異なる機能に属している三つの変数に格納しvm.stack
、そのローカル変数を格納するためのそれらの部分を占めていましたか?答えはvm.callstack
あるフィールドbstack
格納要素がそのタイプbcallframe
を入力します。次のように後者が定義されます。
typedef struct {
bvalue *func; /* function register pointer */
bvalue *top; /* top register pointer */
bvalue *reg; /* base register pointer */
binstruction *ip; /* instruction pointer (only berry-function) */
int status;
} bcallframe;
スタックフレームの機能を実装するために使用される構造は、各フィールドの関数です。
func
:関数呼び出しに現在を指しますvm.stack
(関数呼び出しが中に押し込まれる前の位置vm.stack
中央)。top
:関数のスタックポインタを指し示すvm.stack
位置。スタックポインタは、常に最後のスタック領域後の値の関数を指します。reg
:にスタックベースポインタの機能vm.stack
場所。よりまたは同等の機能スタック常に小さい第一のスタック空間の位置の関数にベースポインタ値ポイント。ip
:命令ポインタ。VMはまた、命令ポインタの機能が保存され、したがって、このフィールドを設定する必要が連動、関数呼び出しが発生すると、現在の命令ポインタの関数です。status
:関数のスタックフレームのためのいくつかのステータスフラグ。
いくつかの情報は、仮想マシンの状態に圧入し、各関数呼び出しの呼び出し機能が発生しているvm.callstack
と呼ばれる機能の状態に復帰した後に再開することができるようにするために。この情報は、機能スタックベースアドレス、命令ポインタ、スタック等を含みます。
変数スタック
されている変数、スタック、vm.stack
VMの作成時に割り当てられた、vm.stacktop
と視野点vm.stack
最後の要素は、それがスタック変数の総容量に関する情報が含まれています。ベリー変数スタックは、あなただけのスタック容量がVMに小さい作成、および実装プロセスが動的に調整されたときに、変数、動的な拡張をサポートしています。スタック拡張は、一般的に関数が呼び出されたとき、インタプリタのチェック機能は、スタックの容量を拡張するかどうかを決定する必要が発生します。拡張が発生した場合は、次のような処理を実行します。
- 再割り当て変数とデータスタックをコピーします。
- 更新
vm.callstack
のすべての要素func
、top
およびreg
ドメインを。 - 開いているすべての上位値を更新し
value
たドメインを。
前記ステップ2と3は、スタック内の値を更新する必要がある構造点/変数の参照を挙げます。具体的な実装では、参照することができbe_stack_expansion()
、ソース機能。
可変スタック障害
可変スタック故障が変更されたスタック変数展開リード要素のアドレスを参照し、プログラムが間違っている現象を実行している間、可変要素へのスタック・ポインタはので、更新されていません。GitHubの上や非行#42も、この問題について説明します。
ベリーコード自体は、まだいくつかの変数のエラー・スタックの失敗を存在してもよいです。この問題を回避するために、我々は、スタック変数の故障につながる可能性の状況を要約したものです。
- ベリー関数呼び出し。APIのベリー関数呼び出しのさまざまな発生し、そのため、問題のコールスタックの障害があります。
be_val2str
、be_tostring()
などの文字列変換関数は、インスタンスを呼び出すことができるtostring
方法。- あなたは、コールスタックの障害も発生する可能性があり、デストラクタトリガGCを呼ぶかもしれません。
- あなたがオブジェクトを作成するときにGC GCは、トリガすることができます。このような行動は、文字列、閉鎖、マップリストなどを作成しています。
- マニュアルスケーリング変数スタック機会
以下の例は、コールスタックの失敗の問題を考慮していません。
- 使用が、
be_realloc()
インタフェース(be_malloc()
また、トリガーそれGCによって実現することができる)が、この場合は、デストラクタを呼び出さないので、呼び出しスタックの故障を引き起こしません。 - (使用したベリーの公開APIをフルに活用
BERRY_API
修正)を、代わりに直接ポインタ変数を使用してインデックスを使用してのAPIのこのタイプは、その変数のスタック故障を心配しないでください。換言すれば、唯一ベリー内部コードは、変数、スタック故障の問題を解決するために必要。