Outline
Call stack for local variables and all other functions during the execution of the call chain on call information stored function. Berry calls the call stack, stack refers specifically to the script, rather than the C call stack.
In be_vm.h can be seen in the fields VM structure and associated call stack:
struct bvm {
// ...
bvalue *stack; /* stack space */
bvalue *stacktop; /* stack top register */
bstack callstack; /* function call stack */
// ...
};
stack
And stacktop
for maintaining a local store stack variables (hereinafter referred to as "variable stack", refers to a function of stack space vm.stack
for a period of the function space is used), and callstack
is a function of stack frame of the stack.
We use a simple script to explain the action of the field:
def func1(c)
return c + 1
end
def func2(b)
return func1(b) + 2
end
def func3(a)
return func2(a) + 3
end
When we perform the func3(10)
time, to perform func1
the internal call chain up:
call stack top
+-------------------------+
| function: func1 |
| local variable(s): c |
+-------------------------+
| function: func2 |
| local variable(s): b |
+-------------------------+
| function: func3 |
| local variable(s): a |
+-------------------------+
call stack base
Obviously, all local variables on the function call chain should be stored to ensure that the calling function can continue to be returned after the called function. All the local variables on the function call chain is arranged in a calling sequence, values of these variables are stored in vm.stack
the. So vm.stack
is a bvalue
array. When the call reaches the deepest when the chain, the variables in the vm.stack
arrangement is as follows:
stack index | 0 | 1 | 2 |
variable | func3:a | func2:b | func1:c |
Here we must note that vm.stack
stored in the three variables belong to different functions, so to determine how each function in vm.stack
occupied those parts to store its local variables? The answer is a vm.callstack
field that is bstack
the type that stores elements bcallframe
type. The latter is defined as follows:
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;
The structure used to implement the stack frame function, the function of each field is:
func
: Refers to the current in the calling functionvm.stack
the position (before calling function will be pressed into thevm.stack
middle).top
: The function stack pointer, pointing tovm.stack
a location. Stack pointer always points to a function of the value after the last stack space.reg
: The function of the stack base pointer tovm.stack
a location. Base pointer value points to a function of the position of the first stack space, which is always less than or equal function stack.ip
: Instruction pointer. VM is also a function of the current instruction pointer, the function call occurs, the function of the instruction pointer ganged need to be saved, and therefore set this field.status
: Some status flag for the function stack frame.
Have some information pressed into the virtual machine state and calling function of each function call occurs vm.callstack
in order to be able to resume after the return to the state of the called function. This information includes the function stack base address, instruction pointer stack and the like.
Variable stack
Variable stack, which is vm.stack
assigned when the VM is created, vm.stacktop
field points to vm.stack
the last element, it contains information on the total capacity of the stack variables. Berry variable stack supports dynamic expansion, when variable you just created a stack capacity is small in VM, and the implementation process will dynamically adjust. Stack expansion generally occurs when the function is called, the interpreter checks the function needs to decide whether to expand the capacity of the stack. If the expansion occurs perform the following process:
- Reallocate variable and copy the data stack.
- Update
vm.callstack
all of the elementsfunc
,top
andreg
domains. - Update all open upvalue the
value
domain.
Wherein steps 2 and 3 mentioned structural point / variable references a value in the stack needs to be updated. Specific implementation can refer to be_stack_expansion()
the source function.
Variable stack failure
Variable stack failure refers to a stack variable expansion leads element address has changed, and the stack pointer to the variable element has not updated so that while the program is running the wrong phenomenon. On GitHub AND DELINQUENCY # 42 also describes this problem.
Berry code itself may still exist some variables error stack failure. To avoid this problem, we summarize the situation may lead to failure of the stack variables:
- Berry function call. A variety of API Berry function call occurs, and therefore there is a problem call stack failure.
be_val2str
,be_tostring()
Such as string conversion functions may call the instancetostring
method.- You might call the destructor trigger GC, call stack failure may also occur.
- GC GC may trigger when you create an object. Such actions include creating a string, closures, Map List, and the like.
- Manual scaling variable stack occasion
The following cases do not consider the issue of the failure of the call stack:
- Although the use of
be_realloc()
an interface (be_malloc()
may also be implemented by a trigger when it GC), but this case does not call the destructor, and therefore does not cause failure of the call stack. - Full use of Berry's public API (using
BERRY_API
modified), this type of API instead of directly using an index using pointer variables, so do not worry about variable stack failure. In other words, only Berry internal code required to solve the problem of variable stack failure.