Press enter to see results or esc to cancel.

Processing Object Types

Turbo Pascal includes many functions and procedures to process object type declaration. The reason is in the complexity of object type. It can contain fields, methods, constructors, destructors, private and public members, static methods, dynamic methods, virtual methods, etc.

Function ProcessObjectTypeDeclaration (TypeIdentifier: PIdentifier): PTypeDefinition;
Type TProc2 = Procedure  (MethodIdentifierData: PProcedureIdentifierData; Index: Word);
Var ObjectType, AncestorTypeDefinition: PObjectTypeDefinition;
    Saved_LastPointerToTypeDefinitionOffset: Word;
    Last_UnitW30: Word;
    UnitIdentifierData: PUnitIdentifierData;
    AncestorUnitTypeOffset: TUnitOffsets;
    ObjectDataType: TIntegerTypeSet;
    Data_W02, Data_W10, Data_W14, Data_W1E, Data_W20, Data_W22, Data_W24: Word;
begin
  Saved_LastPointerToTypeDefinitionOffset := LastPointerToTypeDefinitionOffset;
  If CurrentProcedureIdentifier <> 0 then Error (LocalObjectTypesAreNotAllowed);
  GetNextToken;
  ObjectType := CreateTypeDefinition (SizeOf (TObjectTypeDefinition), 0, [], btObject);
  With ObjectType^ do
    begin
      TypeIdentifierOffset := Ofs (TypeIdentifier^);
      W1A := 0;
      W1C := 0;
    end;
  Last_UnitW30 := 0;
  If (SourceType <> stUnitImplementation) or (LocalDebugSymbols in ModuleCompilerSwitches) then
    begin
      Last_UnitW30 := PUnitHeader (Ptr (Seg (ObjectType^), 0))^.W30;
      PUnitHeader (Ptr (Seg (ObjectType^), 0))^.W30 := Ofs (ObjectType^);
    end;
  ObjectType^.W18 := Last_UnitW30;
  CurrentRecordOrObjectTypeDefinitionOffset := Ofs (ObjectType^);
  If CheckAndGetNextToken (Token_LeftParenthesis) then
    begin
      AncestorTypeDefinition := PObjectTypeDefinition (ExpectTypeIdentifier);
      If AncestorTypeDefinition^.BaseType <> btObject then Error (ObjectTypeExpected);
      ExpectTokenAndGetNext (Token_RightParenthesis);
      ObjectDataType := [];
      UnitIdentifierData := Ptr (Seg (AncestorTypeDefinition^), AncestorTypeDefinition^.W24);
      Data_W24 := UnitIdentifierData^.UnitSegment;
      Data_W22 := AncestorTypeDefinition^.W22;
      UnitIdentifierData := Ptr (Seg (AncestorTypeDefinition^), AncestorTypeDefinition^.W20);
      Data_W20 := UnitIdentifierData^.UnitSegment;
      Data_W1E := AncestorTypeDefinition^.W1E;
      Data_W14 := AncestorTypeDefinition^.OffsetOf_VMT_Offset;
      Data_W10 := AncestorTypeDefinition^.VMT_Size;
      Data_W02 := AncestorTypeDefinition^.Size;
      GetTypeAndUnitIdentifierOffsets (Pointer (AncestorTypeDefinition), AncestorUnitTypeOffset);
    end else begin
               ObjectDataType := [];
               Data_W24 := SystemUnitSegment;
               Data_W22 := $0118;
               Data_W20 := SystemUnitSegment;
               Data_W1E := $0110;
               Data_W14 := $FFFF;
               Data_W10 := $0000;
               Data_W02 := $0000;
               AncestorUnitTypeOffset.UnitAndTypeOffset := 0;
             end;
  With ObjectType^ do
    begin
      DataType := ObjectDataType;
      AncestorTypeOffset := AncestorUnitTypeOffset;
      Size := Data_W02;
      VMT_Size := Data_W10;
      OffsetOf_VMT_Offset := Data_W14;
      W1E := Data_W1E;
      W20 := UnitIdentifierDataOffset (Data_W20);
      W22 := Data_W22;
      W24 := UnitIdentifierDataOffset (Data_W24);
      W0A := 0;
      W1A := 0;
      W1C := 0;
      VMT_TypedConstantsBlockRecordOffset := $FFFF;
      W16 := $FFFF;
      FieldsListOffset := SymbolTable [stMain].NextRecordOffset;
    end;
  OffsetToNextMemberOffset := Ofs (ObjectType^.W0A);
  NumberOfDynamicMethods := 0;
  CreateSymbolTable (8);
  ProcessSectionOfMembers;
  Repeat
    If CheckAndGetNextToken (Token_PUBLIC) then PrivateFlagMask := $00 else
      If CheckAndGetNextToken (Token_PRIVATE) then PrivateFlagMask := $80 else Break;
    ProcessSectionOfMembers;
  until False;
  PrivateFlagMask := $00;
  ExpectTokenAndGetNext (Token_END);
  CreateVMT;
  CurrentRecordOrObjectTypeDefinitionOffset := 0;
  LastPointerToTypeDefinitionOffset := Saved_LastPointerToTypeDefinitionOffset;
  ProcessObjectTypeDeclaration := ObjectType;
end;

Dynamic methods need Dynamic Method Table (DMT) and virtual methods need Virtual Method Table (VMT). Few procedures take care for this.

  Procedure CreateVMT;
  Var VMT: PVMT;
  begin
    With ObjectType^ do If VMT_Size <> 0 then
      begin
        If OffsetOf_VMT_Offset = $FFFF then
          begin
            OffsetOf_VMT_Offset := Size;
            Inc (Size, 2);
          end;
        CreateDMT;
        VMT := IncreaseSymbolTable (stTypedConstants, VMT_Size);
        With VMT^.Header do
          begin
            SizeOfObjectInstance := Size;
            NegativeSizeOfObjectInstance := - Size;
            DMT_Offset := 0;
            AlwaysZero := 0;
          end;
        FillChar (VMT^.VirtualMethodPointer [0], VMT_Size - SizeOf (TVMTHeader), $FF);
        SetOffsetToDMT (0, Ofs (VMT^.Header.DMT_Offset));
        Create_VMT_Entries;
        VMT_TypedConstantsBlockRecordOffset := SymbolTable [stTypedConstantsBlocks].UsedSize;
        CreateTypedConstantsBlockRecord;
      end;
  end;

    Procedure SetOffsetToDMT (B: Byte; TypedConstantOffset: Word);
    Label AncestorType;
    Var ObjectTypeDefinition: PObjectTypeDefinition;
    begin
      ObjectTypeDefinition := ObjectType;
      If B <> 0 then GoTo AncestorType;
      While ObjectTypeDefinition^.W16 = $FFFF do
        begin

AncestorType:

          If ObjectTypeDefinition^.AncestorTypeOffset.TypeOffset = 0 then Exit;
          ObjectTypeDefinition := PointerFromOffsets (ObjectTypeDefinition^.AncestorTypeOffset);
        end;
      AddReferenceRecordForTypedConstant (Seg (ObjectTypeDefinition^), ObjectTypeDefinition^.W16,
                                          [rfDataSegment, rfConstant, rfOffset], 0, TypedConstantOffset);
    end;

    Procedure Create_VMT_Entries;
    Var ObjectTypeDefinition: PObjectTypeDefinition;
    begin
      Repeat
        ObjectTypeDefinition := ObjectType;
        CallProcedureForEachMethod (ObjectTypeDefinition, Create_VMT_Entry, 0);
        If ObjectTypeDefinition^.AncestorTypeOffset.TypeOffset = 0 then Break;
        ObjectTypeDefinition := PointerFromOffsets (ObjectTypeDefinition^.AncestorTypeOffset);
      until False;
    end;

Procedure Create_VMT_Entry (MethodIdentifierData: PProcedureIdentifierData; Index: Word); Far;
begin
  With MethodIdentifierData^ do If (W8 << 0) and not (pfDynamic in Flags) and
      (Word (Ptr (SymbolTable [stTypedConstants].Segment, LastTypedConstantsSize + W8)^) <> 0) then
    begin
      Word (Ptr (SymbolTable [stTypedConstants].Segment, LastTypedConstantsSize + W8)^) := 0;
      Word (Ptr (SymbolTable [stTypedConstants].Segment, LastTypedConstantsSize + W8 + 2)^) := 0;
      AddReferenceRecordForTypedConstant (Seg (MethodIdentifierData^), ProceduresRecordOffset,
                                          [rfSegment, rfOffset], 0, LastTypedConstantsSize + W8);
    end;
end;

    Procedure Create_DMT_Entries;
    Var ObjectTypeDefinition: PObjectTypeDefinition;
    begin
      Repeat
        ObjectTypeDefinition := ObjectType;
        CallProcedureForEachMethod (ObjectTypeDefinition, Create_DMT_Entry, 0);
        If ObjectTypeDefinition^.AncestorTypeOffset.TypeOffset = 0 then Break;
        ObjectTypeDefinition := PointerFromOffsets (ObjectTypeDefinition^.AncestorTypeOffset);
      until False;
    end;

Procedure Create_DMT_Entry (MethodIdentifierData: PProcedureIdentifierData; Index: Word); Far;
begin
  With MethodIdentifierData^ do If pfDynamic in Flags then
    begin
      Word (Ptr (SymbolTable [stTypedConstants].Segment, CurrentTypedConstantValueOffset + DynamicMethodCounter * 2)^) := W8;
      AddReferenceRecordForTypedConstant (Seg (MethodIdentifierData^), ProceduresRecordOffset,
                    [rfSegment, rfOffset], 0, CurrentTypedConstantValueOffset + NumberOfDynamicMethods * 6);
      Inc (DynamicMethodCounter);
    end;
end;

    Procedure CreateDMT;
    Var DMT: PDMT;
    begin
      If NumberOfDynamicMethods <> 0 then
        begin
           DMT := IncreaseSymbolTable (stTypedConstants, NumberOfDynamicMethods * (SizeOf (Integer) + SizeOf (Pointer)) +
                                                         SizeOf (TDMTHeader));
           With DMT^.Header do
             begin
               Parent_DMT_Offset := 0;
               CachedIndex := 0;
               CachedEntryOffset := 0;
               EntryCount := NumberOfDynamicMethods;
             end;
          CurrentTypedConstantValueOffset := Ofs (DMT^.DynamicMethodIndexTable [0]);
          FillChar (DMT^.DynamicMethodIndexTable [0], NumberOfDynamicMethods * (SizeOf (Integer) + SizeOf (Pointer)), 0);
          SetOffsetToDMT (1, Ofs (DMT^.Header.Parent_DMT_Offset));
          Create_DMT_Entries;
          ObjectType^.W16 := SymbolTable [stTypedConstantsBlocks].UsedSize;
          CreateTypedConstantsBlockRecord;
        end;
    end;

This procedure goes through all object’s methods and calls specified procedure for each method. It is used to check for unique index for dynamic methods and to create entries in DMT and VMT.

  Procedure CallProcedureForEachMethod (ObjectTypeDefinition: PObjectTypeDefinition; Proc2: TProc2; DynamicMethodIndex: Word);
  Var ObjectIdentifier: PIdentifier;
      ObjectIdentifierOfs: Word absolute ObjectIdentifier;
      VariableIdentifierData: PVariableIdentifierData;
      MethodIdentifierData: PProcedureIdentifierData absolute VariableIdentifierData;
  begin
    DynamicMethodCounter := 0;
    ObjectIdentifier := Pointer (ObjectTypeDefinition);
    ObjectIdentifierOfs := ObjectTypeDefinition^.W0A;
    While ObjectIdentifierOfs <> 0 do
      begin
        VariableIdentifierData := PVariableIdentifierData (PChar (ObjectIdentifier) + ObjectIdentifier^.Name.Len + 4);
        Case TToken (Ord (ObjectIdentifier^.Token) and $7F) of
          Token_VariableIdentifier: ObjectIdentifierOfs := VariableIdentifierData^.W5;
          else begin
                 Proc2 (MethodIdentifierData, DynamicMethodIndex);
                 ObjectIdentifierOfs := MethodIdentifierData^.ProcedureTypeDefinition.W06_;
               end;
        end;
      end;
  end;

This procedure processes members of Turbo Pascal object: Private and Public directives, fields, methods (procedures and functions), constructors and destructors.

  Procedure ProcessSectionOfMembers;
  Var MethodsDeclared: Boolean;

  begin
    MethodsDeclared := False;
    Repeat
      CheckIfDirecive (_PUBLIC, Token_PUBLIC);
      CheckIfDirecive (_PRIVATE, Token_PRIVATE);
      Case Token of
        Token_PROCEDURE,
        Token_FUNCTION: begin
                          ProcessMethodDeclaration (Token);
                          MethodsDeclared := True;
                          Continue;
                        end;
        Token_CONSTRUCTOR,
        Token_DESTRUCTOR: begin
                            ProcessMethodDeclaration (Token);
                            MethodsDeclared := True;
                            Continue;
                            With PObjectTypeDefinition (Ptr (SymbolTable [stMain].Segment,
                                                             CurrentRecordOrObjectTypeDefinitionOffset))^ do
                              If VMT_Size = 0 then VMT_Size := 8;
                          end;
      end;
      If MethodsDeclared then Exit;
      Case Token of
        Token_PUBLIC,
        Token_PRIVATE,
        Token_END: Exit;
      end;
      ProcessCommaSeparatedFieldsAndType;
      CalculateVariableOffsets;
      ExpectTokenAndGetNext (Token_Semicolon);
      MethodsDeclared := False;
    until False;
  end;

    Procedure ProcessMethodDeclaration (MethodToken: TToken);
    Var MethodIdentifierOffset: Word;
        MethodIdentifier: PIdentifier;
        MethodIdentifierData: PProcedureIdentifierData;
        MethodIdentifierToken: TToken;
        MethodType: PProcedureTypeDefinition;
        AncestorMethodIdentifierData: PProcedureIdentifierData;
    begin
      GetNextToken;
      ExpectIdentifier;
      If CurrentIdentifierDeclaredInCurrentScope (MethodIdentifierOffset, Pointer (MethodIdentifierData),
                                                    MethodIdentifierToken) then
        begin
          If MethodIdentifierToken <> Token_ProcedureIdentifier then Error (DuplicateIdentifier);
          If Ptr (Seg (MethodIdentifierData^), MethodIdentifierData^.OuterBlockProcedureIdentifier) =
             Ptr (SymbolTable [stMain].Segment, CurrentRecordOrObjectTypeDefinitionOffset) then Error (DuplicateIdentifier);
          AncestorMethodIdentifierData := MethodIdentifierData;
        end else AncestorMethodIdentifierData := nil;
      MethodIdentifierData := StoreCurrentIdentifierToSymbolTable (CurrentScopeIdentifierTableAddress, 10, MethodIdentifier);
      GetNextToken;
      MethodIdentifier^.Token := TToken (Ord (Token_ProcedureIdentifier) or PrivateFlagMask);
      MethodIdentifierData^.OuterBlockProcedureIdentifier := CurrentRecordOrObjectTypeDefinitionOffset;
      MethodIdentifierData^.LocalIdentifiersList := SymbolTable [stTemporary].UsedSize;
      Word (Ptr (Seg (MethodIdentifierData^), OffsetToNextMemberOffset)^) := Ofs (MethodIdentifier^);
      OffsetToNextMemberOffset := Ofs (MethodIdentifierData^.ProcedureTypeDefinition.W06_);
      CreateProcedureRecord (MethodIdentifier, MethodIdentifierData);
      Case MethodToken of
        Token_CONSTRUCTOR: MethodIdentifierData^.Flags := [pfConstructor, pfMethod, pfFar];
        Token_DESTRUCTOR : MethodIdentifierData^.Flags := [pfDestructor,  pfMethod, pfFar];
        else               MethodIdentifierData^.Flags := [               pfMethod, pfFar];
      end;
      MethodType := ProcessProcedureHeader (MethodToken);
      MethodType^.Size := 8;
      ExpectTokenAndGetNext (Token_Semicolon);
      If (AncestorMethodIdentifierData = nil) or (AncestorMethodIdentifierData^.W8 = 0) then
        CheckIfVirtualMethod else
          CheckOverridenVirtualMethod;
    end;

      Procedure CheckIfVirtualMethod;
      Var ObjectTypeDefinition: PObjectTypeDefinition;
          DynamicMethodIndex: Word;

      begin
        If CompareIdentifierToDirectiveAndGetNextToken (_VIRTUAL) then
          begin
            ObjectTypeDefinition := Ptr (SymbolTable [stMain].Segment, CurrentRecordOrObjectTypeDefinitionOffset);
            With ObjectTypeDefinition^ do If VMT_Size = 0 then VMT_Size := SizeOf (TVMTHeader);
        If pfConstructor in MethodIdentifierData^.Flags then Error (VirtualConstructorsAreNotAllowed);
            Case Token of
              Token_Semicolon: begin
                                 DynamicMethodIndex := ExpectIntegerConstant;
                                 If DynamicMethodIndex = 0 then Error (DuplicateDynamicMethodIndex);
                                 Repeat
                                   CallProcedureForEachMethod (ObjectTypeDefinition,
                                                               CheckIfUniqueDynamicMethodIndex,
                                                               DynamicMethodIndex);
                                   If ObjectTypeDefinition^.AncestorTypeOffset.TypeOffset = 0 then Break;
                                   ObjectTypeDefinition := PointerFromOffsets (ObjectTypeDefinition^.AncestorTypeOffset);
                                 until False;
                                 MethodIdentifierData^.W8 := DynamicMethodIndex;
                                 Include (MethodIdentifierData^.Flags, pfDynamic);
                               end;
              else begin
                     MethodIdentifierData^.W8 := ObjectTypeDefinition^.VMT_Size;
                     Inc (ObjectTypeDefinition^.VMT_Size, SizeOf (Pointer));
                   end;
            end;
            ExpectTokenAndGetNext (Token_Semicolon);
          end else MethodIdentifierData^.W8 := 0;
      end;

      Procedure CheckOverridenVirtualMethod;
      Var DynamicMethodIndex: Word;
      begin
        If CompareIdentifierToDirectiveAndGetNextToken (_VIRTUAL) then
          begin
            If pfDynamic in AncestorMethodIdentifierData^.Flags then
              begin
                If Token = Token_Semicolon then Error (IntegerConstantExpected);
                DynamicMethodIndex := ExpectIntegerConstant;
                If DynamicMethodIndex <> AncestorMethodIdentifierData^.W8 then
                  Error (HeaderDoesNotMatchPreviousDefinition);
                If (AncestorMethodIdentifierData^.Flags * [pfConstructor, pfDestructor]) <>
                   (MethodIdentifierData^.Flags * [pfConstructor, pfDestructor]) then
                     Error (HeaderDoesNotMatchPreviousDefinition);
                If not CheckProcedureTypeCompatibility (@MethodIdentifierData^.ProcedureTypeDefinition,
                                                        @AncestorMethodIdentifierData^.ProcedureTypeDefinition) then
                  Error (HeaderDoesNotMatchPreviousDefinition);
                ExpectTokenAndGetNext (Token_Semicolon);
                MethodIdentifierData^.W8 := DynamicMethodIndex;
                MethodIdentifierData^.Flags :=
                  MethodIdentifierData^.Flags + AncestorMethodIdentifierData^.Flags * [pfDynamic, pf0100];
              end;
          end else Error (VIRTUAL_Expected);
      end;

Dynamic method indexes need to be unique.

Procedure CheckIfUniqueDynamicMethodIndex (MethodIdentifierData: PProcedureIdentifierData; Index: Word); Far;
begin
  With MethodIdentifierData^ do
    If pfDynamic in Flags then If Index = W8 then Error (DuplicateDynamicMethodIndex);
end;