📄 internal.doc
字号:
VT Internals DocumentationDescription-----------This file documents some of the methods used by VT to implement itsfeatures.Organization------------The following topics are covered: memory management, primitivefunctions, the interpreter, the compiler, and VTC efficiency notes.Memory management-----------------The majority of VT's automatic memory management is done withreference counting. Leaks are possible through self-referential arraystructures, so there is also a garbage collector. VT code thatmanipulates arrays, regexps, strings or programs must maintain thereference counts for these structures so that they are not deletedprematurely or leaked.Data structures that are deleted at a predetermined time are"backlisted"-- that is, they store a list of VTC data frames thatrefer to them. User nodes (remotes, windows, key bindings, files) andsome arrays are backlisted. Pointers to user nodes and backlistedarrays in the VT program should not be left lying around unless theyare the primary pointers used to find those objects or are encased inDframe structures, because the backlisting scheme is designed tohandle Dframes used by VTC code. When backlisted objects are deleted,the frames that refer to them are reset to NULL values. Backlists arestored in dynamically allocated linked chains.Arrays are reference-counted if they are alloc() arrays. The gvarsarray is never deleted, so it is not reference-counted. Parameter andlocal variable arrays are deleted at a predetermined time (when thefunction exits) and are backlisted.Strings are reference-counted at two levels. The Rstr structurecontains a dynamically-allocated string and a reference count, and isused to keep track of the physical representations of VTC strings. AnIstr structure contains a pointer to an Rstr and a reference count;each Istr represents a copy of the string from the point of view ofthe interpreter. Although several Istrs can point to a single Rstr,the interpreter and primitive functions must make sure that changes toone Istr do not affect the other Istrs pointing to the same Rstr.This is accomplished by passing the Istr to isolate() before makingmodifications to it. isolate() makes a separate copy of the string (anew Rstr) if the current Rstr has more than one Istr pointing to it.When the reference count for an Rstr drops to zero (no copies of thestring are referenced by VTC pointers), the string is freed; when thereference count for an Istr drops to zero (that particular copy of astring is no longer referenced by any VTC pointers) the Istr structureis freed.Most of the memory management in the VT program is handled through theref_frame() and deref_frame() functions and their companion functionsref_frames() and deref_frames(). These examine the types of theirarguments, which are data frames or arrays of data frames, and do theappropriate referencing or dereferencing.One sometimes annoying constraint of the memory management system isthat the programmer must be careful to increment the reference countof data frames before decrementing the counts of any other data framesthat might point to the same objects. As an example, when a primitivefunction returns, the interpreter must do a ref_frame() on the returnvalue before calling deref_frames() on the arguments that it mustremove to make room to place the return value on the data stack. Ifit called deref_frames() first, the object that the return valuepointed to might be deleted.Another constraint is that, because backlists store the locations ofdata frames that point to them, the memory manager must be informedwhen data frames move. After the interpreter calls ref_frame() on thereturn value of a primitive function and deletes the arguments thatwere passed to the primitive, it adds the return value to the top ofthe stack. Since it has just moved the value from one Dframestructure to another, it must call move_frame_refs() to updatebacklists that contained the location of the return value.Fortunately, move_frame_refs() takes essentially zero time unless itis dealing with a backlisted object, and even then in the case of thereturn value from primitive functions, the reference will always be atthe front of the backlist chain since it has just been added.Some organized memory management is done in the compiler. Allidentifiers read by the tokenizer are indexed in a resizable table andfreed as a group when the parser is finished. Other than that, theparser allocates and frees memory on an as-needed basis.In general, VT's memory management makes intensive use of dynamic allocation, and in particular repeated allocation and freeing of small data structures. For this reason, VT uses a tray malloc. Tray malloc simply allocates structures of 16 bytes or less from trays. This saves lots of time, and probably some space as well.Primitive functions-------------------Primitive functions and operator functions communicate with theinterpreter through Dframes. They generally do not need to worryabout reference counts unless they are modifying data structures thathold other Dframes.A primitive function's definition begins with:void pr_name(rf, argc, argv) Dframe *rf, *argv; int argc;In prmt.h this is abbreviated "PDECL(pr_name)". rf is the returnframe, and is initialized to type F_NULL before the primitive oroperator function is called. So a primitive need do nothing to returna value of NULL. argc and argv are the arguments-- if the primitiveis specified as accepting a certain number of arguments (the argcelement of the primitive structure is positive) in the table inprmtab.c, argc is guaranteed to be that number.Primitives can access up to four arguments easily through the Dp1,Dp2, Dp3 and Dp4 macros, which stand for argv[0], argv[1], argv[2] andargv[3] respectively. In addition, Dp is an abbreviation for Dp1 forprimitives which take only one argument. Tp and Tp1..Tp4 areabbreviations for the types of Dp1..Dp4, Int and Int1..Int4 for theinteger values for integer data frames, and Un and Un1..Un4 for theuser node values.The first step of most primitive functions is to type-check itsarguments. This is not done systematically because a significantnumber of primitives accept a variety of different argument types.The macro Terror("name") displays an error message in the currentwindow and returns a message to the interpreter to abort.Primitive functions need not deal with adding rf to backlists orincrementing reference counts of objects it is returning, because theinterpreter automatically calls ref_frame() on rf after the primitivefunction returns. However, if they are assigning a data frame to adata structure such as an array element or a window/remote object,they must call ref_frame() on the data frame they assigned to.Interpreter-----------The VTC interpreter is a fairly simple stack machine that operates ona data stack (dstack) and a call stack (cstack). The interpreter isreentrant, so that the data stack and call stack may have data andcall frames from several different programs at the same time. A callframe that is the starting frame of a program is called a "detached"frame, and a postcondition of the interp() function is that thedetached frame closest to the top of the call stack will have beenremoved, along with all the call frames above it, when interp() exits.The call frames in the call stack contain enough information todetermine which data frames are associated with which programs on thecall stack.The programs the interpreter reads are a sequence of one-word
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -