ctordtor.txt
来自「开放源码的编译器open watcom 1.6.0版的源代码」· 文本 代码 · 共 579 行 · 第 1/2 页
TXT
579 行
Ctor/Dtor Design
================
This file documents the use the constructors (CTOR) and destructors (DTOR).
It includes the design for error-handling.
History
-------
91/11/21 - JWW - initial design
Common Stuff
============
typedef void CTOR_DTOR(); // ctor/dtor function
struct THREAD_CTL // controls each execution thread
{ CTOR_BLOCK_LIST *autos; // - active CTOR blocks for autos
TRY_BLOCK_REF *tries; // - active try blocks
char exception[1]; // - exception (variable-sized)
}
Run-time Globals
================
THREAD_CTL thread; // current execution thread
Automatic Objects
=================
- CTOR called for object as the definition is executed
- corresponding DTOR must be called when block is exited (or popped by error
handling)
- need structure to indicate which items have been CTOR'd (view block
as C++ object which needs to CTOR'd on entry, DTOR'd on exit, and is
modified as each automatic object in the scope has been CTOR'd)
struct CTOR_BLOCK_LIST // list of items CTOR'd (on stack)
CTOR_BLOCK_LIST *prev; // - previous block
BLOCK_DTORS *dtors; // - destructors list
CTOR_BLOCK_ITEM *list[1]; // - list of elements CTOR'd
};
struct BLOCK_DTORS // items to be dtor'd (static r/o)
{ unsigned count; // - # objects
CTOR_DTOR *list[1]; // - dtor's
};
- the two lists must correspond
- implementation:
- CTOR_BLOCK_LIST allocated as an automatic object (on stack) for each
block (if req'd)
- CTOR'd on block entry by:
ctor_block_list( CTOR_BLOCK_LIST *list
, BLOCK_DTORS *dtors
, unsigned count );
- all item pointers are set to NULL to indicate not CTOR'd
- links to previous block for error handling
- DTOR'd on block exit by:
dtor_block_list( CTOR_BLOCK_LIST *list );
dtor_block_list_curr();
- after a successful CTOR of an item:
ctor_block_item( CTOR_BLOCK_LIST *list
, void *item
, unsigned index );
ctor_block_item_curr( void *item
, unsigned index );
- method:
- goto out of block
- generates "IC_BLOCK_EXIT scope" for each scope exited
- execution of definition:
- generates "IC_BLOCK_CTOR sym" after each class variable CTOR'D
- execution of IC_BLOCK_OPEN:
- inspects scope to see if CTOR_BLOCK_LIST req'd
- allocates automatic variable if req'd
- execution of IC_BLOCK_EXIT
- inspects scope to see if CTOR_BLOCK_LIST req'd
- generates call to dtor_block_item_curr, if req'd
- execution of IC_BLOCK_CLOSE
- inspects scope to see if CTOR_BLOCK_LIST req'd
- generates call to dtor_block_item_curr, if req'd
- execution of IC_BLOCK_CTOR
- generates call to ctor_block_item_curr
- note: perhaps we should set flag somewhere to indicate that a class
element CTOR has occurred in a scope (this could be done when
IC_BLOCK_CTOR is generated by the front-end)
Error Handling
==============
- type of throw expression determines which of the active catch-blocks
receives control
- there is a need to uniquely identify (CATCH_NAME) each possible type
that can be caught
typedef char *CATCH_NAME;
- implementation:
- in a COMDEF generate a unique 1-byte global variable whose name
corresponds to the type (note: T, const T, T&, const T& cause
matches )
- two names are generated for bare-name references
- if we never use the storage in the COMDEF, the linker could be
upgraded to support "link-time enumeration"
- we could use the storage to speed the search for a catch
- need structure to record an active try-block:
struct TRY_BLOCK_REF // reference (on stack) to try block
{ TRY_BLOCK_REF *prev; // - previous active block
TRY_BLOCK *try_block; // - reference to block
}
struct TRY_BLOCK // an active try (static r/o)
{ unsigned count; // - # catch blocks
CATCH_ITEM list[1]; // - list of catch blocks
}
struct CATCH_ITEM // an active catch block for a try
void (*rtn)(); // - start of code
CATCH_NAME catch_name; // - unique name
}
- need structure to record possible throw expressions which can be caught:
struct THROW_LIST // list of expression types (static r/o)
{ unsigned count; // - # possible expressions
THROW_EXPR list[1]; // - possible expressions
}
struct THROW_EXPR // define possible expression (static r/o)
{ void (*copy)(); // - copy routine
CATCH_NAME catch_name; // - unique name
};
- implementation:
- view try block as C++ object
- block entry:
ctor_try_block( TRY_BLOCK_REF *ref, TRY_BLOCK *try_block );
- block exit:
dtor_try_block( TRY_BLOCK_REF *ref );
dtor_try_block_curr();
- throwing an exception
- how does a DTOR get called for the expression ?
- if no expression:
- if error-handling active, call rethrow_exception();
- does this use the converted expression ?
- if no, a new THROW_LIST is required
- else call unexpected()
- else causes call:
- if error-handling active
- do we free old exception
- call throw_exception( THROW_LIST *list, expression );
- search back through TRY_BLOCK_REF blocks to locate CATCH_BLOCK and the
TRY_BLOCK_REF for the item.
- if none: call unexpected();
- unwind stack (described later) and jump to catch block
- catch block processing: same as any other block
Compilation
===========
try { ctor_try_block( &try_block );
... stuff for { ... }
} goto lab_4;
lab_1:
catch( object &o ) { object internal;
object &o = internal;
ctor_catch( &internal );
[ ctor_block_item_curr( &o, index ); ] if req'd
... stuff for { ... }
[ dtor_block_item_curr(); ] if req'd
} goto lab_4;
lab_2:
catch( object o ) { object o;
ctor_catch( &o );
[ ctor_block_item_curr( &o, index ); ] if req'd
... stuff for { ... }
[ dtor_block_item_curr(); ] if req'd
} goto lab_4;
lab_3:
catch( ... ) { ... stuff for { ... }
goto lab_4;
}
lab_4;
- the copy constructor for the indicated object is invoked by the run-time
routine "ctor_catch"
Stack Unwinding:
================
- the active CTOR_BLOCK_LIST items must be processed until one preceding
the active try block is encountered
- for each such CTOR_BLOCK_LIST, call the appropriate destructor for the
CTOR'd items
Compiling CTOR's
=================
- housekeeping may be required:
- have to set up pointers to virtual base tables
- have to set up pointers to virtual function tables
- a constructor may only partially initialize an object before an exception
is thrown
- the initialized parts must be destructed (in reverse order)
- we need structures to record what has been initialized:
- use CTOR_BLOCK_LIST, BLOCK_DTORS to record this
- this can be accomplished by using a second CTOR_BLOCK_LIST within the
outermost block of a constructor; each constructor call is followed by an
appropriate "ctor_block_item_curr" call
- no modifications are required for stack unwinding
- note: the same BLOCK_DTORS is used for all ctor's/dtor's for a class (there
need be only one per class)
- implementation: ctor (can be many)
- to start, generate everthing in-line (what follows is optimized version)
- in declaration order, generate initialization for base classes, then
for class members, then generate function-body code
- if mem-initializers exist, (base classes, class members)
- fill in VFPTR pointers if required
- generate in-line initialization code based on specifications
else
- if more than one ctor exists
- fill in VBPTR, VFPTR pointers if required
- call generated-default-ctor
else
- generate default ctor in-line
- all virtual base classes (pg.92) are constructed before any
non-virtual base classes
- second parameter (0,1) passed to such constructors; when 1, the
exact type of the class is known
- virtual bases are constructed
- construct all virtuals
- subsequent calls to constructors pass 0 to ensure that the
virtuals are not re-initialized
- generated-default-ctor: internally-named routine which is equivalent to
C::C() when this constructor does not exist (so, it must have a
different name than C::C() that is specified.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?