Class DBTraceCodeManager

All Implemented Interfaces:
ErrorHandler, DBTraceManager, DBTraceDelegatingManager<DBTraceCodeSpace>, TraceCodeManager, TraceCodeOperations

The implementation of TraceCodeManager for DBTrace

The "fluent" interfaces actually create quite a burden to implement here; however, we have some opportunity to extract common code among the various views. There are a few concepts and nuances to consider in order to handle all the fluent cases. The manager implements TraceCodeOperations directly, which means it must provide a version of each TraceCodeUnitsView that composes all memory address spaces. These are named with the suffix MemoryView and extend AbstractBaseDBTraceCodeUnitsMemoryView.

In addition, in order to support getCodeSpace(AddressSpace, boolean), it must provide a version of each that can be bound to a single memory address space. Same for getCodeRegisterSpace(TraceThread, int, boolean). These are named with the suffix View and extend AbstractBaseDBTraceCodeUnitsView.

Furthermore, there are three types of views:

  1. Those defined by a table, i.e., defined data and instructions. These extend AbstractBaseDBTraceDefinedUnitsView.
  2. Those defined implicitly, but may have a support table, i.e., undefined units. This is implemented by DBTraceUndefinedDataView.
  3. Those defined as the composition of others, i.e., data and defined units. These extend AbstractComposedDBTraceCodeUnitsView.

The first two types represent a view of a single code unit type, so they both extend AbstractSingleDBTraceCodeUnitsView.

The abstract classes do not nominally implement the trace manager's TraceBaseCodeUnitsView nor TraceBaseDefinedUnitsView interfaces, because Java prevents the (nominal) implementation of the same interface with different type parameters by the same class. E.g., DBTraceDataView would inherit TraceBaseCodeUnitsView<DBTraceData> via AbstractBaseDBTraceCodeUnitsView, but also TraceBaseCodeUnitsView<TraceDataUnit> via TraceDataView. Instead, the abstract classes structurally implement those interfaces, meaning they implement the methods required by the interface, but without naming the interface in their `implements` clause. The realizations, e.g., DBTraceDataView, nominally implement their corresponding interfaces, meaning they do name the interface. Each realization will inherit the structural implementation from the abstract classes, satisfying the requirements imposed by nominally implementing the interface.

Note, as a result, navigating from declarations in the interfaces to implementations in abstract classes using your IDE may not work as expected :/ . The best way is probably to display the type hierarchy of the interface declaring the desired method. Open one of the classes implementing it, then display all its methods, including those inherited, and search for desired method.

Here is the type hierarchy presented with notes regarding structural interface implementations:

The view composition is not hierarchical, as each may represent a different combination, and one type may appear in several compositions. The single-type views are named first, then the composed views:

  • Instructions - single-type view
  • Defined Data - single-type view
  • Undefined Data - single-type view

Note that while the API presents separate views for defined data and undefined units, both are represented by the type TraceData. Meaning, a client with a data unit in hand cannot determine whether it is defined or undefined from its type alone. It must invoke Data.isDefined() instead. While the implementation provides a separate type, which we see mirrors the hierarchy of the views' implementation, the client interfaces do not.

  • Code Units - Instructions, Defined Data, Undefined Data
  • Data - Defined Data, Undefined Data
  • Defined Units - Instructions, Defined Data

The MemoryView classes compose the memory address spaces into a single view. These need not mirror the same implementation hierarchy as the views they compose. Other than special handling for compositions including undefined units, each memory view need not know anything about the views it composes. There are two abstract classes: AbstractBaseDBTraceCodeUnitsMemoryView, which is suitable for composing views without undefined units, and AbstractWithUndefinedDBTraceCodeUnitsMemoryView, which extends the base making it suitable for composing views with undefined units. The realizations each extend from the appropriate abstract class. Again, the abstract classes do not nominally implement TraceBaseCodeUnitsView. They structurally implement it, partly satisfying the requirements on the realizations, which nominally implement their appropriate interfaces.