Two used units exporting a class of the same name previously errored with "Duplicate type name". Give types the same cross-unit semantics as consts and vars: the units coexist, a bare reference binds to the unit later in `uses` (last-in-uses wins), and a qualified `Unit.TName` reference binds to that specific unit's own type — with distinct typeinfo, vtable, field cleanup and method dispatch. Semantic: - DefineTypeLastWins detaches the prior unit's type on a cross-unit collision (kept in the per-unit cache for qualified access) and installs the later unit as the flat winner; same-unit redeclaration stays a hard error. The duplicate-method and impl-body-link guards skip foreign-owned entries (with a signature fallback so a generic instance whose template lives in another unit still links). FindMethodDecl and a one-shot owner hint on ResolveMethodOverload bind a call to the method on the type that actually resolved, not the first-registered. FindTypeOrInstantiate resolves a qualified type name through the directed per-unit lookup BEFORE the flat FindType (which would strip the qualifier to the uses-chain winner). - TTypeDesc gains OwningUnit (stamped on Define); the AST type decl gains ResolvedDesc; a method-call expr gains QualifierUnit (set by the parser for `Unit.Type.Create`), so a qualified constructor resolves against its unit. Codegen (QBE and native): a type's storage symbols (typeinfo / vtable / _FieldCleanup / __cn) are mangled by the unit currently being emitted, and qualified reference sites mangle by the resolved descriptor's owner — so two used units' same-named types emit and are referenced under distinct, non-colliding symbols. Generic instances stay unprefixed. Tests: e2e cross-unit type last-wins (+ reversed) and qualified disambiguation; an IR-level check that the two types emit distinct symbols.
2702 lines
103 KiB
ObjectPascal
2702 lines
103 KiB
ObjectPascal
{
|
|
Blaise - An Object Pascal Compiler
|
|
Copyright (c) 2026 Graeme Geldenhuys
|
|
SPDX-License-Identifier: Apache-2.0 WITH Swift-exception
|
|
Licensed under the Apache License v2.0 with Runtime Library Exception.
|
|
See LICENSE file in the project root for full license terms.
|
|
}
|
|
|
|
unit uAST;
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, contnrs, uSymbolTable;
|
|
|
|
type
|
|
{ Base node — all AST nodes carry source position. }
|
|
TASTNode = class
|
|
public
|
|
Line: Integer;
|
|
Col: Integer;
|
|
end;
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
{ Expressions }
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
{ Semantic analyser fills ResolvedType on every expression node. }
|
|
TASTExpr = class(TASTNode)
|
|
public
|
|
[Unretained] ResolvedType: TTypeDesc; { not owned — points into the
|
|
symbol table's type pool, which
|
|
outlives the AST. }
|
|
end;
|
|
|
|
TIntLiteral = class(TASTExpr)
|
|
public
|
|
Value: Int64;
|
|
IsUInt64: Boolean; { True when the source literal exceeds MaxInt64 and
|
|
must be interpreted as an unsigned 64-bit value.
|
|
Set by the parser; Value carries the bit pattern. }
|
|
end;
|
|
|
|
TFloatLiteral = class(TASTExpr)
|
|
public
|
|
Value: string; { raw text e.g. '3.14' or '1.5E-3'; parsed at codegen }
|
|
end;
|
|
|
|
TStringLiteral = class(TASTExpr)
|
|
public
|
|
Value: string;
|
|
IsCharCoerce: Boolean; { set by uSemantic when used as Byte in a comparison }
|
|
CharOrdValue: Integer; { OrdAt(Value, 0) — valid only when IsCharCoerce = True }
|
|
end;
|
|
|
|
TStringSubscriptExpr = class(TASTExpr)
|
|
public
|
|
StrExpr: TASTExpr; { owned }
|
|
IndexExpr: TASTExpr; { owned }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TArrayLiteralExpr = class(TASTExpr)
|
|
public
|
|
Elements: TObjectList; { owned list of TASTExpr }
|
|
IsConstArray: Boolean; { set by uSemantic when this bracket literal is an
|
|
'array of const' argument — its elements are boxed
|
|
into TVarRec records at the call site rather than
|
|
forming a homogeneous open array. }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ A `lo..hi` range appearing as an element of a set literal, e.g.
|
|
[1..3] or [Red..Blue]. Both bounds must be compile-time constants;
|
|
uSemantic.AnalyseSetLiteralExpr expands the range into individual
|
|
member nodes (so codegen never sees this node), and rejects reversed
|
|
or non-constant ranges. Carried through the .bif serialiser because a
|
|
generic template body may contain an unexpanded range. }
|
|
TSetRangeExpr = class(TASTExpr)
|
|
public
|
|
LowExpr: TASTExpr; { owned }
|
|
HighExpr: TASTExpr; { owned }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TNilLiteral = class(TASTExpr); { nil keyword — type is tyNil }
|
|
|
|
TParamMode = (
|
|
pmNone,
|
|
pmVar,
|
|
pmRecordValue,
|
|
pmStaticArrayValue,
|
|
pmJumboSetValue { by-value jumbo (>64) set param: by-ref aggregate ABI }
|
|
);
|
|
|
|
{ TMemberVisibility is declared in uSymbolTable (so the symbol-table
|
|
descriptors can carry it without a circular unit dependency); uAST re-uses
|
|
that type for the parser-level AST member decls. }
|
|
|
|
TIdentExpr = class(TASTExpr)
|
|
public
|
|
Name: string;
|
|
ParamMode: TParamMode; { set by uSemantic — how the param slot is laid out }
|
|
IsConstant: Boolean; { set by uSemantic — True if this ident is a skConstant symbol }
|
|
ConstValue: Int64; { valid when IsConstant = True; integer/bool/enum value }
|
|
ConstString: string; { valid when IsConstant = True and type is tyString }
|
|
IsGlobal: Boolean; { set by uSemantic — this ident is a program-level global var }
|
|
IsThreadVar: Boolean; { set by uSemantic — threadvar (TLS) global }
|
|
IsImplicitSelf: Boolean; { set by uSemantic — bare field name implicitly referencing Self }
|
|
[Unretained] ImplicitFieldInfo: TObject; { TFieldInfo — not owned; valid when IsImplicitSelf }
|
|
IsImplicitSelfMethod: Boolean; { set by uSemantic — bare zero-arg method call on Self }
|
|
[Unretained] ImplicitMethodDecl: TObject; { TMethodDecl — not owned; set when IsImplicitSelfMethod }
|
|
IsMetaclassRef: Boolean; { set by uSemantic — bare class type identifier used as
|
|
metaclass value (typeinfo ptr); codegen emits
|
|
$typeinfo_<Name> instead of loading a variable. }
|
|
ConstArraySymbol: string; { set by uSemantic — non-empty when this ident resolves
|
|
to an array const; the mangled QBE data-label codegen
|
|
must reference instead of $Name. }
|
|
QualifierUnit: string; { set by the parser — non-empty when written as a
|
|
unit-qualified reference 'Unit.Symbol'. uSemantic
|
|
resolves the name against this specific unit's
|
|
exports (per-unit cache) instead of the uses chain,
|
|
so a same-named const in another unit can't shadow it. }
|
|
ResolvedOwnerUnit: string; { set by uSemantic — for a module-scope global var,
|
|
the owning unit of the symbol actually resolved (the
|
|
uses-chain last-wins winner for a bare ref, or the
|
|
named unit for a qualified ref). Codegen mangles the
|
|
global's storage symbol with this owner so two used
|
|
units exporting the same var name reference distinct
|
|
slots. Not serialised — recomputed at analysis time;
|
|
empty falls back to the bare-name resolution. }
|
|
end;
|
|
|
|
TFieldAccessExpr = class(TASTExpr)
|
|
public
|
|
RecordName: string; { used when Base = nil (leaf access) }
|
|
FieldName: string;
|
|
Base: TASTExpr; { owned — when non-nil, chained access (e.g. A.B.C) }
|
|
FieldInfo: TFieldInfo; { set by uSemantic — nil for constructor calls }
|
|
IsConstant: Boolean; { set by uSemantic — TypeName.ConstName resolves to a class constant }
|
|
ConstValue: Int64; { valid when IsConstant = True }
|
|
ConstString: string; { valid when IsConstant = True and type is tyString }
|
|
ConstArraySymbol: string; { non-empty for class-level array const — global data label }
|
|
[Unretained] ConstArrayType: TObject; { TStaticArrayTypeDesc — not owned; set when ConstArraySymbol is set }
|
|
IsConstructorCall: Boolean; { set by uSemantic — TypeName.Create }
|
|
IsClassAccess: Boolean; { set by uSemantic — pointer deref needed }
|
|
PropRead: TPropertyInfo; { non-nil if this is a method-backed property read }
|
|
PropOwnerType: string; { class type name for method-backed property calls }
|
|
PropAccessorVSlot: Integer; { set by uSemantic — getter vtable slot, -1 = static call }
|
|
IsImplicitSelf: Boolean; { set by uSemantic — RecordName is a field of Self }
|
|
[Unretained] ImplicitBaseInfo: TFieldInfo; { non-owned — the field of Self holding the record/class }
|
|
IsMethodCall: Boolean; { set by uSemantic — FieldName is a zero-arg method }
|
|
[Unretained] ResolvedMethod: TObject; { TMethodDecl — not owned; set when IsMethodCall }
|
|
IsInterfaceCall: Boolean; { set by uSemantic — zero-arg method call through interface itab }
|
|
[Unretained] ResolvedClassType: TTypeDesc; { not owned; set when IsInterfaceCall — the interface descriptor }
|
|
IsGlobal: Boolean; { set by uSemantic — RecordName is a program-level global }
|
|
IsClassNameAccess: Boolean; { set by uSemantic — .ClassName built-in on a class instance }
|
|
IsClassTypeAccess: Boolean; { set by uSemantic — .ClassType built-in: returns metaclass (typeinfo ptr) }
|
|
IsBuiltinToString: Boolean; { set by uSemantic — .ToString built-in: virtual dispatch via vtable slot 1 }
|
|
IsVarParam: Boolean; { set by uSemantic — RecordName is a var parameter (record by-ref) }
|
|
PropIndexExpr: TASTExpr; { owned — non-nil = indexed property read (e.g. List.Items[i]) }
|
|
IsCharAccess: Boolean; { set by uSemantic — PropIndexExpr indexes a string field: S.Field[N] }
|
|
IsArrayAccess: Boolean; { set by uSemantic — PropIndexExpr subscripts an array field: R.Arr[I] }
|
|
IsClassVarRead: Boolean; { set by uSemantic — TypeName.StaticVar read; lower as a global load of ClassVarEmitName }
|
|
ClassVarEmitName: string; { mangled global label for an IsClassVarRead access }
|
|
IsStaticPropGet: Boolean; { set by uSemantic — TypeName.StaticProp read; ResolvedMethod is the static getter }
|
|
BackingFieldRedirect: Boolean; { set by uSemantic — FieldName was rewritten from a
|
|
property name to its (possibly private) backing
|
|
field; visibility was already enforced on the
|
|
property, so re-analysis of this node must NOT
|
|
re-check the backing field's visibility. Transient
|
|
analysis flag — not serialised. }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TIsExpr = class(TASTExpr)
|
|
public
|
|
Obj: TASTExpr; { owned — left-hand side; must be class instance }
|
|
TypeName: string; { right-hand side type name; resolved by uSemantic }
|
|
[Unretained] ResolvedTargetType: TTypeDesc; { set by uSemantic — class or interface descriptor }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TAsExpr = class(TASTExpr)
|
|
public
|
|
Obj: TASTExpr; { owned — left-hand side; must be class instance }
|
|
TypeName: string; { right-hand side type name; resolved by uSemantic }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ Supports(Obj, IFoo) — Boolean interface-membership test.
|
|
Supports(Obj, IFoo, Ref) — test + assign fat pointer to Ref on success. }
|
|
TSupportsExpr = class(TASTExpr)
|
|
public
|
|
Obj: TASTExpr; { owned — object being tested }
|
|
IntfTypeName: string; { interface type name (second arg) }
|
|
OutVarName: string; { variable to receive fat pointer (third arg; empty = 2-arg form) }
|
|
[Unretained] ResolvedIntfType: TTypeDesc; { set by uSemantic — must be tyInterface }
|
|
OutVarIsGlobal: Boolean; { set by uSemantic }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ `inherited Method(args)` used in EXPRESSION position (the value of the
|
|
parent method's result is needed). The statement form is
|
|
TInheritedCallStmt; this is its expression sibling. Resolved and lowered
|
|
the same way (static dispatch to the parent slot), but yields a value. }
|
|
TInheritedCallExpr = class(TASTExpr)
|
|
public
|
|
Name: string; { parent method name to call }
|
|
Args: TObjectList; { owned TASTExpr items }
|
|
{ Set by uSemantic: }
|
|
[Unretained] ResolvedParentType: TObject; { TRecordTypeDesc — not owned }
|
|
[Unretained] ResolvedMethod: TObject; { TMethodDecl — not owned }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ boSlash is the `/` operator: real division, always yields a float.
|
|
boDiv is the `div` operator: integer division between integer operands. }
|
|
TBinaryOp = (boAdd, boSub, boMul, boSlash, boDiv, boMod,
|
|
boEQ, boNE, boLT, boGT, boLE, boGE,
|
|
boAnd, boOr, boXor, boIn, boShl, boShr, boSar);
|
|
|
|
TBinaryExpr = class(TASTExpr)
|
|
public
|
|
Op: TBinaryOp;
|
|
Left: TASTExpr; { owned }
|
|
Right: TASTExpr; { owned }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TNotExpr = class(TASTExpr)
|
|
public
|
|
Expr: TASTExpr; { owned — must be Boolean }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
{ Statements }
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
TASTStmt = class(TASTNode);
|
|
|
|
TAssignment = class(TASTStmt)
|
|
public
|
|
Name: string;
|
|
Expr: TASTExpr; { owned }
|
|
IsVarParam: Boolean; { set by uSemantic — True if target is a var parameter }
|
|
IsGlobal: Boolean; { set by uSemantic — True if target is a program-level global }
|
|
IsThreadVar: Boolean; { set by uSemantic — True if target is a threadvar }
|
|
[Unretained] ResolvedLhsType: TTypeDesc; { set by uSemantic — type of the target variable }
|
|
IsWeakLhs: Boolean; { set by uSemantic — True if the LHS symbol
|
|
was declared [Weak]; codegen emits a
|
|
_WeakAssign in place of the strong
|
|
addref/release pattern. }
|
|
ImplicitSelfField: TObject; { TFieldInfo — non-nil when LHS is bare field (implicit Self) }
|
|
ResolvedOwnerUnit: string; { set by uSemantic — owning unit of a module-scope global
|
|
var target; codegen mangles the store address with it so
|
|
same-named cross-unit globals write distinct slots. See
|
|
the matching field on TIdentExpr. }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TIfStmt = class(TASTStmt)
|
|
public
|
|
Condition: TASTExpr; { owned }
|
|
ThenStmt: TASTStmt; { owned }
|
|
ElseStmt: TASTStmt; { owned; nil if no else }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TCompoundStmt = class(TASTStmt)
|
|
public
|
|
Stmts: TObjectList; { owned TASTStmt }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ An inline-assembler block: the verbatim text of one `asm` ... `end` body.
|
|
The front end never interprets Code — it is GNU/AT&T assembly handed to the
|
|
backend's assembler verbatim (see docs/inline-asm-design.adoc). }
|
|
TAsmStmt = class(TASTStmt)
|
|
public
|
|
Code: string; { verbatim assembly text between asm and end }
|
|
end;
|
|
|
|
TWhileStmt = class(TASTStmt)
|
|
public
|
|
Condition: TASTExpr; { owned }
|
|
Body: TASTStmt; { owned }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TRepeatStmt = class(TASTStmt)
|
|
public
|
|
Body: TCompoundStmt; { owned — statements between repeat and until }
|
|
Condition: TASTExpr; { owned — the until condition }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TForStmt = class(TASTStmt)
|
|
public
|
|
VarName: string;
|
|
IsGlobal: Boolean; { set by uSemantic — VarName is a program-level global }
|
|
StartExpr: TASTExpr; { owned }
|
|
EndExpr: TASTExpr; { owned }
|
|
IsDownTo: Boolean;
|
|
Body: TASTStmt; { owned }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TForInStmt = class(TASTStmt)
|
|
public
|
|
VarName: string;
|
|
VarIsGlobal: Boolean; { set by semantic — VarName is program-level global }
|
|
CollExpr: TASTExpr; { owned — collection expression }
|
|
Body: TASTStmt; { owned }
|
|
{ Annotations set by the semantic pass }
|
|
[Unretained] ResolvedVarType: TTypeDesc; { element type }
|
|
{ Class-enumerator path (IsArrayIter = False) }
|
|
IsArrayIter: Boolean; { True when collection is a static array }
|
|
EnumVarName: string; { synthetic enumerator slot, e.g. __forin_0 }
|
|
ResolvedEnumTypeName: string; { enumerator class type name }
|
|
[Unretained] GetEnumDecl: TObject; { TMethodDecl — not owned }
|
|
[Unretained] MoveNextDecl: TObject; { TMethodDecl — not owned }
|
|
[Unretained] CurrentDecl: TObject; { TMethodDecl getter — not owned }
|
|
{ Static-array iteration path (IsArrayIter = True) }
|
|
IdxVarName: string; { synthetic index slot — shared with string/dynarray path }
|
|
ArrayLow: Integer; { compile-time lower bound }
|
|
ArrayHigh: Integer; { compile-time upper bound }
|
|
{ Dynamic-array iteration path (IsDynArrayIter = True) }
|
|
IsDynArrayIter: Boolean;
|
|
{ String byte-iteration path (IsStringIter = True) }
|
|
IsStringIter: Boolean;
|
|
{ String codepoint-iteration path (IsCodePointIter = True) }
|
|
IsCodePointIter: Boolean;
|
|
AdvVarName: string; { synthetic slot for byte-advance count }
|
|
{ Set iteration path (IsSetIter = True) }
|
|
IsSetIter: Boolean;
|
|
SetBitCount: Integer; { number of bits in the set (= enum member count) }
|
|
SetMaskVarName: string; { synthetic slot: holds the evaluated set mask
|
|
(small set), or the set's ADDRESS (jumbo) }
|
|
SetIsJumbo: Boolean; { True when the set is >64 members: the slot
|
|
holds a pointer and membership is tested via
|
|
the _SetIn RTL helper, not a register shift }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TTryFinallyStmt = class(TASTStmt)
|
|
public
|
|
TryBody: TCompoundStmt; { owned }
|
|
FinallyBody: TCompoundStmt; { owned }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ One 'on [VarName :] TypeName do Stmt' arm inside a try/except block.
|
|
VarName is empty when the source uses the no-binding form 'on TFoo do'. }
|
|
TExceptHandlerClause = class
|
|
public
|
|
VarName: string; { bound variable name, or '' }
|
|
TypeName: string; { exception class name }
|
|
Body: TCompoundStmt; { owned }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TTryExceptStmt = class(TASTStmt)
|
|
public
|
|
TryBody: TCompoundStmt; { owned }
|
|
{ Typed handlers (on E: T do): non-empty when the except block contains
|
|
'on' clauses. Mutually exclusive with ExceptBody. }
|
|
Handlers: TObjectList; { owned list of TExceptHandlerClause }
|
|
ElseBody: TCompoundStmt; { owned; catch-all after typed handlers, or nil }
|
|
{ Plain catch-all body: non-nil only when the except block has no 'on' clauses. }
|
|
ExceptBody: TCompoundStmt; { owned }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TRaiseStmt = class(TASTStmt)
|
|
public
|
|
Expr: TASTExpr; { owned; nil = bare re-raise }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ 'exit' statement — returns from the current procedure/function.
|
|
Value is the Exit(X) function-result shorthand: the parser stores the raw
|
|
X here. The semantic pass validates it and builds ResultAssign (a
|
|
synthesised 'Result := X'); codegen emits ResultAssign before the exit
|
|
jump. Both nil = bare Exit. }
|
|
TExitStmt = class(TASTStmt)
|
|
public
|
|
Value: TASTExpr; { owned; nil = bare exit; raw parsed expr }
|
|
ResultAssign: TASTStmt; { owned; synthesised 'Result := Value' (TAssignment) }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ 'break' statement — exits the innermost loop. }
|
|
TBreakStmt = class(TASTStmt);
|
|
|
|
{ 'continue' statement — jumps to next iteration of the innermost loop. }
|
|
TContinueStmt = class(TASTStmt);
|
|
|
|
{ One arm of a case statement: one or more ordinal values → body }
|
|
TCaseBranch = class
|
|
public
|
|
Values: TObjectList; { owned list of TASTExpr (integer/enum literal values) }
|
|
Stmt: TASTStmt; { owned }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TCaseStmt = class(TASTStmt)
|
|
public
|
|
Selector: TASTExpr; { owned — ordinal or string }
|
|
Branches: TObjectList; { owned list of TCaseBranch }
|
|
ElseStmt: TASTStmt; { owned; nil if no else clause }
|
|
IsStringCase: Boolean; { set by uSemantic when Selector resolves to string;
|
|
codegen uses _StringEquals instead of ceqw }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TFieldAssignment = class(TASTStmt)
|
|
public
|
|
RecordName: string;
|
|
FieldName: string;
|
|
Expr: TASTExpr; { owned }
|
|
ObjExpr: TASTExpr; { owned — when non-nil, receiver is this expression (e.g. typecast) }
|
|
FieldInfo: TFieldInfo; { set by uSemantic — carries offset + type }
|
|
IsClassAccess: Boolean; { set by uSemantic — pointer deref needed }
|
|
IsImplicitSelf: Boolean; { set by uSemantic — RecordName is a field of Self }
|
|
[Unretained] ImplicitBaseInfo: TFieldInfo; { non-owned — the field of Self that holds the record/class }
|
|
IsGlobal: Boolean; { set by uSemantic — RecordName is a program-level global }
|
|
IsVarParam: Boolean; { set by uSemantic — RecordName is a var parameter (record by-ref) }
|
|
PropIndexExpr: TASTExpr; { owned — non-nil = indexed property write via setter,
|
|
OR (IsElemWrite) the element index into an array-typed field }
|
|
[Unretained] PropWriteInfo: TPropertyInfo; { non-owned — set by semantic when PropIndexExpr is set }
|
|
PropOwnerType: string; { owner class name for setter call; valid when PropIndexExpr set }
|
|
PropAccessorVSlot: Integer; { set by uSemantic — setter vtable slot, -1 = static call }
|
|
IsElemWrite: Boolean; { set by uSemantic — FieldName is an array-typed field and
|
|
PropIndexExpr selects the element to store into }
|
|
[Unretained] IntfWriteDesc: TTypeDesc; { set by uSemantic — non-nil = interface
|
|
property write: RecordName is interface-typed and
|
|
FieldName has been rewritten to the SETTER method;
|
|
codegen dispatches it through the itab with Expr
|
|
as the single argument }
|
|
IsClassVarWrite: Boolean; { set by uSemantic — qualified STATIC (class-level)
|
|
variable write 'TFoo.StaticVar := V'. The target is
|
|
the single shared global emitted under
|
|
ClassVarEmitName; codegen lowers it exactly like a
|
|
bare global store (delegates to EmitAssignment),
|
|
ignoring RecordName/FieldName. }
|
|
[Unretained] ClassVarLhsType: TTypeDesc; { set with IsClassVarWrite — the
|
|
static var's type, for the delegated store. }
|
|
ClassVarEmitName: string; { set with IsClassVarWrite — the mangled data-label
|
|
of the static var's storage slot. }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ Static-array element write: 'ArrayName[IndexExpr] := ValueExpr' }
|
|
TStaticSubscriptAssign = class(TASTStmt)
|
|
public
|
|
ArrayName: string;
|
|
IndexExpr: TASTExpr; { owned }
|
|
ValueExpr: TASTExpr; { owned }
|
|
BaseExpr: TASTExpr; { owned — when non-nil, the element being written is
|
|
the IndexExpr-th element of the array yielded by
|
|
BaseExpr (an l-value subscript chain whose result
|
|
is an inner static-array address). Used to lower
|
|
multi-dimensional / chained writes A[i][j] := v;
|
|
ArrayName is then unused for base resolution. }
|
|
IsGlobal: Boolean; { set by uSemantic }
|
|
IsVarParam: Boolean; { set by uSemantic — ArrayName is a var/out
|
|
parameter, so the local slot holds the ADDRESS
|
|
of the caller's variable; codegen needs one
|
|
extra dereference to reach the array }
|
|
[Unretained] ResolvedArrayType: TTypeDesc; { set by uSemantic; not owned }
|
|
IsImplicitSelf: Boolean; { set by uSemantic — ArrayName is an array-typed field of Self }
|
|
[Unretained] ImplicitFieldInfo: TFieldInfo; { non-owned — the Self field; set with IsImplicitSelf }
|
|
{ Default array property write: Obj[I] := V where Obj's class has a
|
|
`default` indexed property — lowered to a setter call. When
|
|
PropWriteInfo is non-nil, ArrayName is the receiver variable, IndexExpr
|
|
is the index arg, ValueExpr is the value arg. }
|
|
[Unretained] PropWriteInfo: TObject; { TPropertyInfo — non-owned; nil = plain array write }
|
|
PropOwnerType: string; { declaring class of the setter }
|
|
PropAccessorVSlot: Integer; { setter vtable slot, -1 = static call }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ Write a value through a pointer: 'PtrExpr^ := ValueExpr' }
|
|
TPointerWriteStmt = class(TASTStmt)
|
|
public
|
|
PtrExpr: TASTExpr; { owned — pointer expression }
|
|
ValExpr: TASTExpr; { owned — value to store }
|
|
[Unretained] BaseTy: TTypeDesc; { non-owned — element type; set by uSemantic }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TProcCall = class(TASTStmt)
|
|
public
|
|
Name: string;
|
|
Args: TObjectList; { owned TASTExpr items }
|
|
[Unretained] ResolvedDecl: TObject; { TMethodDecl — not owned; set by uSemantic for user-defined procs }
|
|
IsImplicitSelfMethod: Boolean; { set by uSemantic — call is on Self }
|
|
IsIndirectCall: Boolean; { set by uSemantic — Name is a procedural-typed variable }
|
|
IndirectCallIsGlobal: Boolean; { set by uSemantic — when IsIndirectCall, variable is global }
|
|
[Unretained] ResolvedProcType: TObject; { TProceduralTypeDesc — not owned; valid when IsIndirectCall }
|
|
IsProcFieldCall: Boolean; { set by uSemantic — unqualified Name is a
|
|
procedural-typed field of the current class
|
|
(implicit Self.Field), called as a statement }
|
|
[Unretained] ProcFieldInfo: TFieldInfo; { not owned — the procedural field, when IsProcFieldCall }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TFuncCallExpr = class(TASTExpr)
|
|
public
|
|
Name: string;
|
|
Args: TObjectList; { owned TASTExpr items }
|
|
[Unretained] ResolvedDecl: TObject; { TMethodDecl — not owned; set by uSemantic }
|
|
IsImplicitSelfMethod: Boolean; { set by uSemantic — call is on Self }
|
|
IsIndirectCall: Boolean; { set by uSemantic — Name resolves to a
|
|
variable of procedural type; codegen
|
|
loads the function pointer and calls
|
|
through it. ResolvedProcType holds
|
|
the signature. }
|
|
IndirectCallIsGlobal: Boolean; { set by uSemantic — when IsIndirectCall,
|
|
True if the holding variable is a
|
|
program-level global. }
|
|
[Unretained] ResolvedProcType: TObject; { TProceduralTypeDesc — not owned;
|
|
valid when IsIndirectCall }
|
|
IsBuiltinHasClassAttr: Boolean; { set by uSemantic — HasClassAttribute builtin }
|
|
HasClassAttrClass: string; { class name for arg 1 (class being queried) }
|
|
HasClassAttrAttr: string; { attribute class name for arg 2 }
|
|
IsProcFieldCall: Boolean; { set by uSemantic — unqualified Name is a
|
|
procedural-typed field of the current class
|
|
(implicit Self.Field), used as an expression }
|
|
[Unretained] ProcFieldInfo: TFieldInfo; { not owned — the procedural field, when IsProcFieldCall }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ Call through an arbitrary expression of procedural type: Expr(args).
|
|
Used when the callee is not a simple identifier — e.g. an array element
|
|
(Fns[I]()) or a field access result. ResolvedProcType is set by uSemantic. }
|
|
TIndirectFuncCallExpr = class(TASTExpr)
|
|
public
|
|
CalleeExpr: TASTExpr; { owned — the expression that yields the proc pointer }
|
|
Args: TObjectList; { owned TASTExpr items }
|
|
[Unretained] ResolvedProcType: TObject; { TProceduralTypeDesc — not owned; set by uSemantic }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ Pointer dereference expression: 'PtrExpr^' — result type is BaseType of pointer }
|
|
TDerefExpr = class(TASTExpr)
|
|
public
|
|
Expr: TASTExpr; { owned — the pointer expression }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ Address-of expression: '@Expr' — result type is ^ElemType }
|
|
TAddrOfExpr = class(TASTExpr)
|
|
public
|
|
Expr: TASTExpr; { owned — the inner expression whose address is taken }
|
|
ResolvedFreeRoutine: TObject; { TMethodDecl — not owned; populated by
|
|
uSemantic when Expr is a bare identifier
|
|
naming a standalone routine. Lets codegen
|
|
emit @<MDecl.ResolvedQbeName> instead of
|
|
@<source-name>, which matters once routine
|
|
names get unit-prefixed. }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TMethodCallStmt = class(TASTStmt)
|
|
public
|
|
ObjectName: string;
|
|
Name: string; { method name }
|
|
Args: TObjectList; { owned TASTExpr items }
|
|
ObjExpr: TASTExpr; { owned — when non-nil, receiver is this expression }
|
|
{ Set by uSemantic: }
|
|
[Unretained] ResolvedClassType: TTypeDesc; { not owned }
|
|
[Unretained] ResolvedMethod: TObject; { TMethodDecl — not owned; avoids forward ref }
|
|
{ Set by uSemantic for itab dispatch only: the method's resolved return
|
|
type. A discarded interface/record-returning call in statement
|
|
position still needs the sret calling convention — codegen reads this
|
|
to allocate a throwaway buffer. }
|
|
[Unretained] ResolvedReturnTypeDesc: TTypeDesc; { not owned; nil for procedures }
|
|
IsImplicitSelf: Boolean; { ObjectName is a field of Self }
|
|
[Unretained] ImplicitBaseInfo: TFieldInfo; { not owned — the field of Self }
|
|
IsGlobal: Boolean; { set by uSemantic — ObjectName is a program-level global }
|
|
IsVarParam: Boolean; { set by uSemantic — ObjectName is a var/out parameter }
|
|
IsBuiltinToString: Boolean; { set by uSemantic — built-in TObject.ToString virtual dispatch }
|
|
IsConstructorCall: Boolean; { set by uSemantic — metaclass-var Create in stmt position }
|
|
IsMetaclassDispatch: Boolean; { set by uSemantic — receiver is a metaclass var }
|
|
IsStaticCall: Boolean; { set by uSemantic — TypeName.StaticMethod(): a static
|
|
(class-level) call with NO Self. Codegen lowers it as a
|
|
plain call to the mangled method symbol, no receiver. }
|
|
IsProcFieldCall: Boolean; { set by uSemantic — Name is a procedural-typed field of
|
|
the receiver, invoked directly (F.Handler;). Codegen
|
|
loads the (Code, Data) pair from the field slot and
|
|
dispatches indirectly instead of calling a method. }
|
|
[Unretained] ProcFieldInfo: TFieldInfo; { not owned — the procedural field, when IsProcFieldCall }
|
|
[Unretained] ResolvedProcType: TObject; { TProceduralTypeDesc — not owned; the field's signature }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ Static dispatch to the parent class's method: 'inherited MethodName(args)'.
|
|
Legal only inside a method body; resolved by uSemantic using the enclosing
|
|
class's parent chain. }
|
|
TInheritedCallStmt = class(TASTStmt)
|
|
public
|
|
Name: string; { parent method name to call }
|
|
Args: TObjectList; { owned TASTExpr items }
|
|
{ Set by uSemantic: }
|
|
[Unretained] ResolvedParentType: TObject; { TRecordTypeDesc — not owned }
|
|
[Unretained] ResolvedMethod: TObject; { TMethodDecl — not owned }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
{ Declarations }
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
{ Constant declaration: const Name = Value; or const Name: Type = Value;
|
|
or const Name: array[IndexType] of ElemType = (v0, v1, ...).
|
|
Also carries a variable initialiser (var G: T = value) — see
|
|
TVarDecl.InitConst — which is why it is declared ahead of TVarDecl. }
|
|
TConstDecl = class(TASTNode)
|
|
public
|
|
Name: string;
|
|
TypeName: string; { non-empty when a type annotation was written }
|
|
IntVal: Int64; { used when kind = integer (bit pattern when IsUInt64) }
|
|
IsUInt64: Boolean; { value exceeded High(Int64): IntVal is the UInt64 bit pattern }
|
|
StrVal: string; { used when IsString = True or IsFloat = True (raw text) }
|
|
IsString: Boolean;
|
|
IsFloat: Boolean; { set when the rhs is a float literal }
|
|
ConstParts: TStringList; { non-nil when const expr has ident refs;
|
|
Objects[i] = nil → string literal,
|
|
Objects[i] <> nil → ident reference }
|
|
{ Array-typed constants — set when TypeName starts with 'array' }
|
|
IsArrayConst: Boolean;
|
|
ArrayIndexType: string; { enum type name used as index, e.g. 'TWeather' }
|
|
ArrayElemType: string; { element type name, e.g. 'string', 'Integer' }
|
|
ArrayElements: TStringList; { ordered list of raw element values (strings or int literals) }
|
|
ArrayIsRangeIndexed: Boolean; { True when index is Low..High integer range }
|
|
ArrayLowBound: Integer;
|
|
ArrayHighBound: Integer;
|
|
{ Multi-dimensional range-indexed const arrays. When the declaration is
|
|
array[L0..H0, L1..H1, ...] of T (comma form) or the equivalent nested
|
|
array[L0..H0] of array[L1..H1] of T, each dimension's bounds are pushed
|
|
onto these parallel lists (one entry per dimension, integer text). When
|
|
non-nil and Count > 1 the constant is multi-dimensional; ArrayElements
|
|
holds the values flattened in row-major order. Count <= 1 (or nil) means
|
|
single-dimension — ArrayLowBound/ArrayHighBound are authoritative. }
|
|
ArrayDimLows: TStringList;
|
|
ArrayDimHighs: TStringList;
|
|
{ Integer bit-op expression in the value position — non-nil when the
|
|
RHS was a chain like 'FG_BLUE or 8' that couldn't be folded at
|
|
parse time (because it references named constants). Tokens are
|
|
stored alternating operand/operator: tokens[0,2,4,...] are operands
|
|
(Objects[i] = nil → integer literal, the string is the int;
|
|
Objects[i] <> nil → ident reference); tokens[1,3,5,...] are
|
|
operator names (one of 'or'/'and'/'xor'/'shl'/'shr'). Semantic
|
|
resolves idents and folds to CD.IntVal. }
|
|
IntExprTokens: TStringList;
|
|
{ Integer constant expression in the value position — non-nil when the RHS
|
|
is a compile-time arithmetic/bitwise expression with operator precedence
|
|
and/or parentheses (e.g. '2 + 3 * 4', '(1 shl 8) or 7', 'BASE * 2').
|
|
Stored as a normal expression AST so precedence and grouping are encoded
|
|
structurally; the semantic pass folds it to CD.IntVal via EvalConstIntExpr
|
|
(resolving any named-constant references after all consts are declared).
|
|
Supersedes the flat IntExprTokens form for newly-parsed expressions; the
|
|
token form is retained only for the legacy single-precedence bit-op path. }
|
|
IntValueExpr: TASTExpr;
|
|
{ Parallel to ArrayElements — each non-nil entry is an IntExprTokens-
|
|
shaped TStringList describing the bit-op expression for that
|
|
element. Semantic folds it and overwrites ArrayElements[i] with
|
|
the resolved integer. Nil indicates the matching ArrayElements[i]
|
|
is already a final scalar (literal, typecast, or ident). }
|
|
ArrayElementParts: TObjectList;
|
|
{ Set-valued constant — set when the RHS is a set literal '[a, b, ...]'.
|
|
SetElements holds the member identifier names (empty for '[]').
|
|
Semantic resolves each to its enum ordinal, ORs (1 shl ord) into IntVal
|
|
(the bitmask), and gives the symbol a tySet type — either CD.TypeName's
|
|
declared set type or the set type inferred from the members' enum. }
|
|
IsSet: Boolean;
|
|
SetElements: TStringList; { non-nil when IsSet; member ident names }
|
|
{ JUMBO (>64-member) set constant: the bitmap as one decimal byte value per
|
|
bitmap byte (RawByteSize entries). Set by uSemantic when IsSet and the
|
|
set type is jumbo, because the bitmask no longer fits in IntVal (Int64).
|
|
Nil for small sets (which keep using IntVal). }
|
|
ConstSetBytes: TStringList;
|
|
{ Canonical QBE data-label for an array const, set by uSemantic. Mangled
|
|
to a unique symbol so identically-named consts in different scopes (and
|
|
RTL-internal consts) do not collide at link time. Empty for non-array
|
|
consts. }
|
|
ResolvedQbeName: string;
|
|
{ Canonical, mangled QBE data-label for a JUMBO set const's byte blob
|
|
(mirrors ResolvedQbeName for array consts). Empty otherwise. }
|
|
ResolvedSetQbeName: string;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TVarDecl = class(TASTNode)
|
|
public
|
|
Names: TStringList; { owned — one or more names: x, y: Integer }
|
|
TypeName: string;
|
|
[Unretained] ResolvedType: TTypeDesc; { set by uSemantic; nil until analysed }
|
|
Attributes: TStringList; { owned — raw attribute names as written in
|
|
source, e.g. 'Weak', 'SomeOther'; the
|
|
Attribute suffix (if present) is preserved
|
|
verbatim — normalisation happens in
|
|
semantic analysis. }
|
|
IsWeak: Boolean; { set by uSemantic when [Weak] is resolved
|
|
on this declaration. Codegen keys off
|
|
this instead of walking Attributes. }
|
|
IsUnretained: Boolean; { set by uSemantic when [Unretained] is resolved.
|
|
Non-owning reference, no ARC and no weak
|
|
registry. See TFieldInfo.IsUnretained. }
|
|
IsGlobal: Boolean; { set by uSemantic when this is a program-level global variable }
|
|
IsThreadVar: Boolean; { set by parser when declared in a threadvar section }
|
|
InitConst: TConstDecl; { owned — optional initialiser for an initialised
|
|
variable (var G: T = value). nil when the
|
|
declaration has no initialiser. Reuses the
|
|
const value/fold/data-emit pipeline; semantic
|
|
type-checks it against ResolvedType and (for
|
|
aggregates) mints InitConst.ResolvedQbeName. }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
{ Type section }
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
{ Abstract base for type definitions. }
|
|
TASTTypeDef = class(TASTNode);
|
|
|
|
{ Type alias / pointer-type alias: type PFoo = ^TFoo; or type TMyInt = Integer;
|
|
TypeName holds the right-hand side as parsed by ParseTypeName, e.g. '^TFoo'. }
|
|
TTypeAliasDef = class(TASTTypeDef)
|
|
public
|
|
TypeName: string;
|
|
end;
|
|
|
|
{ Set type definition: type TOptions = set of TEnum; }
|
|
TSetTypeDef = class(TASTTypeDef)
|
|
public
|
|
BaseTypeName: string; { element type name — must resolve to an enum }
|
|
end;
|
|
|
|
{ Enum type definition: type TDir = (dNorth, dSouth, dEast, dWest);
|
|
or with explicit ordinals: type TCode = (Idle=10, Running=20, Done=30);
|
|
Members holds the member names; Members.Objects[I] carries the ordinal
|
|
value as Pointer(PtrUInt(Value)). When no explicit value is given for a
|
|
member the parser assigns previous+1 (auto-increment), matching Delphi/FPC. }
|
|
TEnumTypeDef = class(TASTTypeDef)
|
|
public
|
|
Members: TStringList; { owned — ordered member names; Objects[I] = ordinal }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure AddMember(const AName: string; AValue: Integer);
|
|
function OrdinalAt(AIndex: Integer): Integer;
|
|
end;
|
|
|
|
TFieldDecl = class(TASTNode)
|
|
public
|
|
Names: TStringList; { owned — e.g. X, Y: Integer }
|
|
TypeName: string;
|
|
[Unretained] ResolvedType: TTypeDesc; { set by uSemantic }
|
|
Attributes: TStringList; { owned — see TVarDecl.Attributes. }
|
|
IsWeak: Boolean; { set by uSemantic when [Weak] is resolved. }
|
|
IsUnretained: Boolean; { set by uSemantic when [Unretained] is resolved. }
|
|
IsClassVar: Boolean; { set by uParser — declared `static var` (a.k.a.
|
|
class variable): one shared storage slot for the
|
|
whole type, not a per-instance field. Lowered to
|
|
a global slot by uSemantic; never gets an instance
|
|
offset. }
|
|
ClassVarEmitName: string; { set by uSemantic for an IsClassVar field — the
|
|
mangled global data-label (CurrentUnitPrefix +
|
|
Class + '_' + Name). Single source of truth so
|
|
BOTH backends emit the slot under the exact label
|
|
the read/write sites use, without re-deriving the
|
|
unit prefix. }
|
|
Visibility: TMemberVisibility; { set by uParser from the enclosing visibility
|
|
section; default mvPublic. Enforced by
|
|
uSemantic's member-access checks. }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TRecordTypeDef = class(TASTTypeDef)
|
|
public
|
|
Fields: TObjectList; { owned TFieldDecl }
|
|
Methods: TObjectList; { owned TMethodDecl }
|
|
ConstDecls: TObjectList; { owned TConstDecl — record-level constants (static const) }
|
|
IsPacked: Boolean; { True iff declared `packed record` — disables
|
|
field alignment padding and tail padding;
|
|
ARC-managed fields (string/class/intf) still
|
|
retain 8-byte storage alignment }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ Procedural type definition: type T = function(...): X; or
|
|
type T = procedure(...);
|
|
Bare procedural pointers — not 'of object' (method pointers) and not
|
|
'reference to' (anonymous methods); both are out of scope for this
|
|
iteration. }
|
|
TProceduralTypeDef = class(TASTTypeDef)
|
|
public
|
|
Params: TObjectList; { owned TMethodParam }
|
|
ReturnTypeName: string; { '' = procedure, non-empty = function }
|
|
IsFunction: Boolean;
|
|
IsMethodPtr: Boolean; { True iff declared with the 'of object'
|
|
suffix. Method-pointer values carry a
|
|
16-byte (Code, Data) pair instead of a
|
|
bare code pointer. }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TBlock = class(TASTNode)
|
|
public
|
|
TypeDecls: TObjectList; { owned TTypeDecl }
|
|
ConstDecls: TObjectList; { owned TConstDecl }
|
|
Decls: TObjectList; { owned TVarDecl }
|
|
ProcDecls: TObjectList; { owned TMethodDecl — standalone procs/funcs }
|
|
Stmts: TObjectList; { owned TASTStmt }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TMethodParam = class(TASTNode)
|
|
public
|
|
ParamName: string;
|
|
TypeName: string; { element type name when IsOpenArray = True }
|
|
IsVarParam: Boolean; { True = passed by reference ('var' or 'out' keyword) }
|
|
IsConstParam: Boolean; { True = 'const' keyword present }
|
|
IsOutParam: Boolean; { True = 'out' keyword (write-only by-reference); IsVarParam is also True }
|
|
IsOpenArray: Boolean; { True = 'array of T'; TypeName is the element type }
|
|
[Unretained] ResolvedType: TTypeDesc; { set by uSemantic — TOpenArrayTypeDesc when IsOpenArray }
|
|
DefaultValue: TASTExpr; { owned — non-nil when the param has a default value
|
|
('= expr' after the type); restricted to literal forms
|
|
(int/float/string/nil) and named-constant idents. }
|
|
HasDefault: Boolean; { set when this param has a default value but the value
|
|
AST itself is unavailable — e.g. a method imported
|
|
from a cached .bif, which records only that a default
|
|
exists (so MinArity/overload resolution accept fewer
|
|
args) without serialising the default expression. }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TMethodDecl = class(TASTNode)
|
|
public
|
|
Name: string; { method name }
|
|
OwnerTypeName: string; { set by uSemantic — class that defines this method }
|
|
Params: TObjectList; { owned TMethodParam }
|
|
ReturnTypeName: string; { empty = procedure }
|
|
[Unretained] ResolvedReturnType: TTypeDesc; { set by uSemantic; nil = procedure }
|
|
Body: TBlock; { owned unless OwnBody = False }
|
|
OwnBody: Boolean; { False for cloned generic method stubs that share the body }
|
|
IsVirtual: Boolean; { declared with 'virtual' directive }
|
|
IsOverride: Boolean; { declared with 'override' directive }
|
|
IsAbstract: Boolean; { declared with 'abstract' directive; implies virtual }
|
|
IsOverload: Boolean; { declared with 'overload' directive }
|
|
IsPublished: Boolean; { declared inside a 'published' visibility
|
|
section of a class. Used by codegen to
|
|
emit an entry in the published-method
|
|
table, which TObject.MethodAddress
|
|
walks at runtime. }
|
|
ResolvedQbeName: string; { set by uSemantic — mangled QBE symbol name;
|
|
empty string means use Name verbatim }
|
|
OwningUnit: string; { name of the unit that exported this routine;
|
|
empty for program-scope. Set by uSemantic;
|
|
consumed by codegen for cross-unit references. }
|
|
IsExternal: Boolean; { declared with 'external' directive — no body }
|
|
ExternalName: string; { C symbol name from 'external name ''c_foo'''; empty = use Pascal name }
|
|
ExternalLib: string; { library from 'external ''c'' name ''malloc'''; bare name, link layer expands per platform. Also recorded unit-level in LinkLibs. }
|
|
NoStackFrame: Boolean; { declared 'nostackframe' — body is an asm
|
|
block that owns the whole frame; codegen
|
|
emits no compiler prologue/epilogue }
|
|
IsRecordMethod: Boolean; { set by uSemantic — owner type is a record (not a class) }
|
|
IsStatic: Boolean; { set by uParser — declared `static` (class method):
|
|
no implicit Self; type-associated dispatch.
|
|
Distinct from VTableSlot's "static dispatch"
|
|
(= non-virtual); a static method also never
|
|
receives a Self pointer. }
|
|
VTableSlot: Integer; { -1 = static; >=0 = vtable index (set by uSemantic) }
|
|
TypeParams: TStringList; { non-nil = generic function template; owns param names }
|
|
TypeParamConstraints: TStringList; { parallel to TypeParams; '' = unconstrained,
|
|
'class', 'record', or a concrete type name }
|
|
OwnerTypeParams: TStringList; { non-nil = generic owner: 'T' in TList<T>.Add }
|
|
IsInlineCandidate: Boolean; { set by uSemantic — body is small, leaf-only, primitive
|
|
params and locals, no try/loops/raise/nested defs.
|
|
Codegen may inline calls to this function at the call
|
|
site instead of emitting a real call instruction. }
|
|
{ Nested procedures (procedure declared inside another procedure's var block):
|
|
CapturedVars lists the names of outer-scope variables that this nested
|
|
proc reads or writes (set by uSemantic). Codegen passes each as an
|
|
implicit leading 'var' (by-pointer) parameter so the nested function can
|
|
share the outer variable's storage. EnclosingDecl points to the directly
|
|
enclosing standalone proc/function (nil for top-level decls). }
|
|
CapturedVars: TStringList; { owned; nil when no captures }
|
|
[Unretained] EnclosingDecl: TMethodDecl; { not owned — enclosing standalone proc, or nil }
|
|
IsInline: Boolean; { set by uParser — 'inline' directive present }
|
|
CallingConv: string; { set by uParser — 'cdecl'/'stdcall'/'register'/'pascal'/
|
|
'safecall' directive (lowercased); '' = default }
|
|
OwningUnit: string; { set by uSemantic / uSemanticImport — unit that declares this routine }
|
|
IsImplOnly: Boolean; { set by uSemantic — standalone routine declared only in a unit's
|
|
implementation section (no interface forward). Such routines are
|
|
PRIVATE to the unit; overload resolution must not treat another
|
|
unit's same-named impl-only routine as a competing candidate. }
|
|
Visibility: TMemberVisibility; { set by uParser from the enclosing visibility section
|
|
when this is a class/record method; default mvPublic. }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TMethodCallExpr = class(TASTExpr)
|
|
public
|
|
ObjectName: string;
|
|
Name: string; { method name }
|
|
QualifierUnit: string; { set by the parser — unit of a qualified
|
|
receiver type 'Unit.Type.Method(...)', so a
|
|
constructor on a cross-unit same-named type
|
|
resolves against that specific unit. }
|
|
Args: TObjectList; { owned TASTExpr }
|
|
ObjExpr: TASTExpr; { owned — receiver expression when ObjectName = '' }
|
|
[Unretained] ResolvedClassType: TTypeDesc; { not owned; set by uSemantic }
|
|
[Unretained] ResolvedMethod: TObject; { TMethodDecl — not owned }
|
|
IsConstructorCall: Boolean; { set by uSemantic — TypeName.Create(args) }
|
|
IsMetaclassDispatch: Boolean; { set by uSemantic — receiver is a metaclass var }
|
|
IsStaticCall: Boolean; { set by uSemantic — TypeName.StaticMethod(): static
|
|
(class-level) call with NO Self; plain call to mangled name }
|
|
IsGlobal: Boolean; { set by uSemantic — ObjectName is a program-level global }
|
|
IsVarParam: Boolean; { set by uSemantic — ObjectName is a var/out parameter }
|
|
IsBuiltinToString: Boolean; { set by uSemantic — built-in TObject.ToString virtual dispatch }
|
|
IsBuiltinInheritsFrom: Boolean; { set by uSemantic — SomeClass.InheritsFrom(OtherClass) }
|
|
IsProcFieldCall: Boolean; { set by uSemantic — Name is a procedural-typed field }
|
|
[Unretained] ProcFieldInfo: TFieldInfo; { not owned — the procedural field }
|
|
[Unretained] ResolvedProcType: TObject; { TProceduralTypeDesc — not owned }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ One property declaration inside a class body. }
|
|
TPropertyDecl = class(TASTNode)
|
|
public
|
|
Name: string; { property name }
|
|
TypeName: string; { declared type }
|
|
ReadName: string; { backing field or getter method; '' = no read accessor }
|
|
WriteName: string; { backing field or setter method; '' = read-only }
|
|
IndexParamName: string; { '' = non-indexed property }
|
|
IndexTypeName: string; { type name of the index parameter; '' when non-indexed }
|
|
IsDefault: Boolean; { declared with the `default` directive (Obj[I] sugar) }
|
|
IsStatic: Boolean; { set by uParser — declared `static`: sugar over a
|
|
static getter/setter; no implicit Self. }
|
|
Visibility: TMemberVisibility; { set by uParser from the enclosing visibility
|
|
section; default mvPublic. }
|
|
end;
|
|
|
|
TClassTypeDef = class(TASTTypeDef)
|
|
public
|
|
ParentName: string;
|
|
ImplementsNames: TStringList; { owned — names of implemented interfaces }
|
|
ConstDecls: TObjectList; { owned TConstDecl — class-level constants }
|
|
Fields: TObjectList; { owned TFieldDecl }
|
|
Methods: TObjectList; { owned TMethodDecl }
|
|
Properties: TObjectList; { owned TPropertyDecl }
|
|
Attributes: TStringList; { owned — class-level custom attribute names e.g. 'Threaded' }
|
|
IsForward: Boolean; { True for a forward decl 'TFoo = class;' (no ancestor,
|
|
no body) — completed by the full decl later in scope }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ Generic type template: type TBox<T> = class ... end }
|
|
TGenericTypeDef = class(TASTTypeDef)
|
|
public
|
|
ParamNames: TStringList; { owned — type parameter names, e.g. ['T'] or ['K','V'] }
|
|
ParamConstraints: TStringList; { owned — parallel to ParamNames; '' = unconstrained }
|
|
ClassDef: TClassTypeDef; { owned — template class body with unresolved param types }
|
|
DefUnitName: string; { set by uSemantic/uSemanticImport at registration —
|
|
unit (or program) that declares the template; the
|
|
Line fields of cloned method bodies refer to THIS
|
|
unit's source, so codegen attributes allocation
|
|
sites to it }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ One concrete instantiation of a standalone generic function — stored on TProgram.
|
|
Codegen iterates this list to emit function bodies. }
|
|
TGenericFuncInstance = class
|
|
public
|
|
InstName: string; { raw name e.g. 'Identity<Integer>' }
|
|
MethodDecl: TMethodDecl; { owned — concrete method decl with substituted types }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ A monomorphised generic METHOD instance (method-level <T>): a method that
|
|
declares its own type parameters, instantiated at a call site. OwnerType
|
|
is the class/record the method belongs to; the instance keeps an implicit
|
|
Self and is emitted as OwnerType_Method_<args>. }
|
|
TGenericMethodInstance = class
|
|
public
|
|
OwnerType: string; { the class/record name }
|
|
InstName: string; { method name with type args, e.g. 'Pick<Integer>' }
|
|
MethodDecl: TMethodDecl; { owned — concrete method decl with substituted types }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ One concrete instantiation produced by monomorphization — stored on TProgram.
|
|
Codegen iterates this list to emit typeinfo, vtables, and method bodies. }
|
|
TGenericInstance = class
|
|
public
|
|
TypeName: string; { raw e.g. 'TBox<Integer>' }
|
|
ClassDef: TClassTypeDef; { owned — cloned class body with substituted type names }
|
|
[Unretained] TypeDesc: TTypeDesc; { non-owned — points to TRecordTypeDesc in SymbolTable }
|
|
DefUnitName: string; { set by uSemantic at instantiation — unit that DECLARES the
|
|
template; method-body Line fields refer to that unit's source }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TInterfaceTypeDef = class(TASTTypeDef)
|
|
public
|
|
ParentName: string;
|
|
Methods: TObjectList; { owned TMethodDecl — forward signatures only }
|
|
Properties: TObjectList; { owned TPropertyDecl — accessors are interface methods }
|
|
IsForward: Boolean; { True for a forward decl 'IFoo = interface;' — completed later in scope }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ Generic interface template: type IFoo<T> = interface ... end }
|
|
TGenericInterfaceDef = class(TASTTypeDef)
|
|
public
|
|
ParamNames: TStringList; { owned — type parameter names, e.g. ['T'] }
|
|
ParamConstraints: TStringList; { owned — parallel to ParamNames; '' = unconstrained }
|
|
IntfDef: TInterfaceTypeDef; { owned — template interface body with unresolved param types }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ Generic record template: type TMyVal<T> = record ... end }
|
|
TGenericRecordDef = class(TASTTypeDef)
|
|
public
|
|
ParamNames: TStringList; { owned — type parameter names, e.g. ['T'] }
|
|
ParamConstraints: TStringList; { owned — parallel to ParamNames; '' = unconstrained }
|
|
RecordDef: TRecordTypeDef; { owned — template record body with unresolved param types }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ One concrete instantiation of a generic record — stored on TProgram.
|
|
Codegen iterates this list to emit method bodies and field cleanup. }
|
|
TGenericRecordInstance = class
|
|
public
|
|
TypeName: string; { raw e.g. 'TMyVal<Integer>' }
|
|
RecordDef: TRecordTypeDef; { owned — cloned record body with substituted type names }
|
|
[Unretained] TypeDesc: TTypeDesc; { non-owned — points to TRecordTypeDesc in SymbolTable }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ One concrete instantiation of a generic interface — stored on TProgram.
|
|
Codegen iterates this list to emit typeinfo data. }
|
|
TGenericInterfaceInstance = class
|
|
public
|
|
InstName: string; { mangled name e.g. 'IEqualityComparer_Integer' }
|
|
IntfDef: TInterfaceTypeDef; { owned — cloned with substituted type names }
|
|
[Unretained] TypeDesc: TTypeDesc; { non-owned — points to TInterfaceTypeDesc in SymbolTable }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TTypeDecl = class(TASTNode)
|
|
public
|
|
Name: string;
|
|
Def: TASTTypeDef; { owned }
|
|
[Unretained] ResolvedDesc: TObject; { TTypeDesc — set by uSemantic to the
|
|
descriptor created for THIS decl. Lets
|
|
codegen emit a unit's own type even when
|
|
a same-named type from another used unit
|
|
won the flat-table slot (FindType by name
|
|
would return the other unit's desc). }
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
{ A library this compilation unit must be linked against — collected by the
|
|
parser from every 'external ''lib''' declaration (interface OR
|
|
implementation). Hoisted to the unit level so an implementation-private
|
|
import still records its link dependency: the private routine stays out of
|
|
the .bif, but the library it needs is serialised via
|
|
TUnitInterface.LinkLibs and propagates to any program that uses the unit.
|
|
LibName is the bare name ('c', 'kernel32') — the link layer expands it per
|
|
platform (-l<name> on ELF, <name>.dll on PE). }
|
|
TLinkLibDecl = class(TASTNode)
|
|
public
|
|
LibName: string;
|
|
end;
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
{ Block and Program }
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
TProgram = class(TASTNode)
|
|
public
|
|
Name: string;
|
|
UsedUnits: TStringList; { owned }
|
|
Block: TBlock; { owned }
|
|
SymbolTable: TSymbolTable; { owned after semantic analysis; nil before }
|
|
GenericInstances: TObjectList; { owned TGenericInstance — populated by uSemantic }
|
|
GenericFuncInstances: TObjectList; { owned TGenericFuncInstance — populated by uSemantic }
|
|
GenericMethodInstances: TObjectList; { owned TGenericMethodInstance — populated by uSemantic }
|
|
GenericIntfInstances: TObjectList; { owned TGenericInterfaceInstance — populated by uSemantic }
|
|
GenericRecordInstances: TObjectList; { owned TGenericRecordInstance — populated by uSemantic }
|
|
LinkLibs: TObjectList; { owned TLinkLibDecl — libraries to link, collected by the parser }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
TUnit = class(TASTNode)
|
|
public
|
|
Name: string;
|
|
SourceFile: string; { absolute path of the .pas file this unit was loaded from }
|
|
UsedUnits: TStringList; { owned — unit names from the interface uses clause }
|
|
ImplUsedUnits: TStringList; { owned — unit names from the implementation
|
|
uses clause (loaded but not re-exported) }
|
|
IntfBlock: TBlock; { owned — forward decls + type decls }
|
|
ImplBlock: TBlock; { owned — full implementations }
|
|
InitStmts: TObjectList; { owned — statements in the initialization section (may be nil) }
|
|
FinalStmts: TObjectList; { owned — statements in the finalization section (may be nil) }
|
|
SymbolTable: TSymbolTable; { owned after standalone semantic analysis;
|
|
nil when the unit is analysed as part of a
|
|
program (program owns the table). }
|
|
GenericInstances: TObjectList;
|
|
GenericFuncInstances: TObjectList;
|
|
GenericMethodInstances: TObjectList;
|
|
GenericIntfInstances: TObjectList;
|
|
GenericRecordInstances: TObjectList;
|
|
LinkLibs: TObjectList; { owned TLinkLibDecl — libraries to link, collected by the parser }
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
function BinaryOpName(AOp: TBinaryOp): string;
|
|
function IsComparisonOp(AOp: TBinaryOp): Boolean;
|
|
|
|
{ Deep-clone helpers for generic-instance method body re-analysis.
|
|
Clones structure + raw textual fields (names, type names, operators).
|
|
Resolved* / FieldInfo / IsXxx semantic annotations are NOT copied: each
|
|
instance must re-run semantic analysis to populate them correctly for
|
|
its own concrete type arguments. }
|
|
function CloneExpr(AExpr: TASTExpr): TASTExpr;
|
|
function CloneStmt(AStmt: TASTStmt): TASTStmt;
|
|
function CloneBlock(ABlock: TBlock): TBlock;
|
|
|
|
{ Granular clone helpers — exposed for uSemanticExport to assemble
|
|
TUnitInterface entries without re-implementing the cloning logic.
|
|
Same deep-copy semantics as CloneBlock: structural + raw textual
|
|
fields are copied, semantic Resolved* annotations are NOT. }
|
|
function CloneTypeDecl(ASrc: TTypeDecl): TTypeDecl;
|
|
function CloneConstDecl(ASrc: TConstDecl): TConstDecl;
|
|
function CloneMethodDecl(ASrc: TMethodDecl): TMethodDecl;
|
|
function CloneMethodParam(ASrc: TMethodParam): TMethodParam;
|
|
function CloneTypeDef(ASrc: TASTTypeDef): TASTTypeDef;
|
|
function CloneClassTypeDef(ASrc: TClassTypeDef): TClassTypeDef;
|
|
|
|
implementation
|
|
|
|
function BinaryOpName(AOp: TBinaryOp): string;
|
|
begin
|
|
case AOp of
|
|
boAdd: Result := '+';
|
|
boSub: Result := '-';
|
|
boMul: Result := '*';
|
|
boSlash: Result := '/';
|
|
boDiv: Result := 'div';
|
|
boMod: Result := 'mod';
|
|
boEQ: Result := '=';
|
|
boNE: Result := '<>';
|
|
boLT: Result := '<';
|
|
boGT: Result := '>';
|
|
boLE: Result := '<=';
|
|
boGE: Result := '>=';
|
|
boAnd: Result := 'and';
|
|
boOr: Result := 'or';
|
|
boXor: Result := 'xor';
|
|
boIn: Result := 'in';
|
|
boShl: Result := 'shl';
|
|
boShr: Result := 'shr';
|
|
boSar: Result := 'sar';
|
|
else
|
|
Result := '?';
|
|
end;
|
|
end;
|
|
|
|
function IsComparisonOp(AOp: TBinaryOp): Boolean;
|
|
begin
|
|
Result := AOp in [boEQ, boNE, boLT, boGT, boLE, boGE];
|
|
end;
|
|
|
|
{ TIfStmt }
|
|
|
|
destructor TIfStmt.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TCompoundStmt }
|
|
|
|
constructor TCompoundStmt.Create;
|
|
begin
|
|
inherited Create();
|
|
Stmts := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TCompoundStmt.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TWhileStmt }
|
|
|
|
destructor TWhileStmt.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TRepeatStmt }
|
|
|
|
destructor TRepeatStmt.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TForStmt }
|
|
|
|
destructor TForStmt.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TForInStmt }
|
|
|
|
destructor TForInStmt.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TTryFinallyStmt }
|
|
|
|
destructor TTryFinallyStmt.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TExceptHandlerClause }
|
|
|
|
destructor TExceptHandlerClause.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TTryExceptStmt }
|
|
|
|
constructor TTryExceptStmt.Create;
|
|
begin
|
|
inherited Create();
|
|
Handlers := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TTryExceptStmt.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TRaiseStmt }
|
|
|
|
destructor TRaiseStmt.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
destructor TExitStmt.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TFieldAccessExpr }
|
|
|
|
destructor TFieldAccessExpr.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TIsExpr }
|
|
|
|
destructor TIsExpr.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TAsExpr }
|
|
|
|
destructor TAsExpr.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TSupportsExpr }
|
|
|
|
destructor TSupportsExpr.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TBinaryExpr }
|
|
|
|
destructor TBinaryExpr.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TNotExpr }
|
|
|
|
destructor TNotExpr.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TAssignment }
|
|
|
|
destructor TAssignment.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TFieldAssignment }
|
|
|
|
destructor TFieldAssignment.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TStaticSubscriptAssign }
|
|
|
|
destructor TStaticSubscriptAssign.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TPointerWriteStmt }
|
|
|
|
destructor TPointerWriteStmt.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TDerefExpr }
|
|
|
|
destructor TDerefExpr.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
destructor TAddrOfExpr.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
destructor TStringSubscriptExpr.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TArrayLiteralExpr }
|
|
|
|
constructor TArrayLiteralExpr.Create;
|
|
begin
|
|
inherited Create();
|
|
Elements := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TArrayLiteralExpr.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
destructor TSetRangeExpr.Destroy;
|
|
begin
|
|
{ LowExpr/HighExpr released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TProcCall }
|
|
|
|
constructor TProcCall.Create;
|
|
begin
|
|
inherited Create();
|
|
Args := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TProcCall.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TFuncCallExpr }
|
|
|
|
constructor TFuncCallExpr.Create;
|
|
begin
|
|
inherited Create();
|
|
Args := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TFuncCallExpr.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TIndirectFuncCallExpr }
|
|
|
|
constructor TIndirectFuncCallExpr.Create;
|
|
begin
|
|
inherited Create();
|
|
Args := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TIndirectFuncCallExpr.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TMethodCallStmt }
|
|
|
|
constructor TMethodCallStmt.Create;
|
|
begin
|
|
inherited Create();
|
|
Args := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TMethodCallStmt.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TInheritedCallStmt }
|
|
|
|
constructor TInheritedCallStmt.Create;
|
|
begin
|
|
inherited Create();
|
|
Args := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TInheritedCallStmt.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TInheritedCallExpr }
|
|
|
|
constructor TInheritedCallExpr.Create;
|
|
begin
|
|
inherited Create();
|
|
Args := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TInheritedCallExpr.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TMethodParam }
|
|
|
|
destructor TMethodParam.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TVarDecl }
|
|
|
|
constructor TVarDecl.Create;
|
|
begin
|
|
inherited Create();
|
|
Names := TStringList.Create();
|
|
Attributes := TStringList.Create();
|
|
IsWeak := False;
|
|
end;
|
|
|
|
destructor TVarDecl.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TFieldDecl }
|
|
|
|
constructor TFieldDecl.Create;
|
|
begin
|
|
inherited Create();
|
|
Names := TStringList.Create();
|
|
Attributes := TStringList.Create();
|
|
IsWeak := False;
|
|
IsUnretained := False;
|
|
end;
|
|
|
|
destructor TFieldDecl.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TRecordTypeDef }
|
|
|
|
constructor TRecordTypeDef.Create;
|
|
begin
|
|
inherited Create();
|
|
Fields := TObjectList.Create(True);
|
|
Methods := TObjectList.Create(True);
|
|
ConstDecls := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TRecordTypeDef.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TProceduralTypeDef }
|
|
|
|
constructor TProceduralTypeDef.Create;
|
|
begin
|
|
inherited Create();
|
|
Params := TObjectList.Create(True);
|
|
IsFunction := False;
|
|
end;
|
|
|
|
destructor TProceduralTypeDef.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TMethodDecl }
|
|
|
|
constructor TMethodDecl.Create;
|
|
begin
|
|
inherited Create();
|
|
Params := TObjectList.Create(True);
|
|
VTableSlot := -1;
|
|
OwnBody := True;
|
|
end;
|
|
|
|
destructor TMethodDecl.Destroy;
|
|
begin
|
|
Params.Free();
|
|
TypeParams.Free();
|
|
TypeParamConstraints.Free();
|
|
OwnerTypeParams.Free();
|
|
CapturedVars.Free();
|
|
if OwnBody then Body.Free();
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TMethodCallExpr }
|
|
|
|
constructor TMethodCallExpr.Create;
|
|
begin
|
|
inherited Create();
|
|
Args := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TMethodCallExpr.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TClassTypeDef }
|
|
|
|
constructor TClassTypeDef.Create;
|
|
begin
|
|
inherited Create();
|
|
ImplementsNames := TStringList.Create();
|
|
ConstDecls := TObjectList.Create(True);
|
|
Fields := TObjectList.Create(True);
|
|
Methods := TObjectList.Create(True);
|
|
Properties := TObjectList.Create(True);
|
|
Attributes := TStringList.Create();
|
|
end;
|
|
|
|
destructor TClassTypeDef.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TInterfaceTypeDef }
|
|
|
|
constructor TInterfaceTypeDef.Create;
|
|
begin
|
|
inherited Create();
|
|
Methods := TObjectList.Create(True);
|
|
Properties := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TInterfaceTypeDef.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TGenericTypeDef }
|
|
|
|
constructor TGenericTypeDef.Create;
|
|
begin
|
|
inherited Create();
|
|
ParamNames := TStringList.Create();
|
|
ParamConstraints := TStringList.Create();
|
|
ClassDef := TClassTypeDef.Create();
|
|
end;
|
|
|
|
destructor TGenericTypeDef.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TGenericInstance }
|
|
|
|
{ TGenericFuncInstance }
|
|
|
|
constructor TGenericFuncInstance.Create;
|
|
begin
|
|
inherited Create();
|
|
end;
|
|
|
|
destructor TGenericFuncInstance.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
constructor TGenericMethodInstance.Create;
|
|
begin
|
|
inherited Create();
|
|
end;
|
|
|
|
destructor TGenericMethodInstance.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TGenericInterfaceDef }
|
|
|
|
constructor TGenericInterfaceDef.Create;
|
|
begin
|
|
inherited Create();
|
|
ParamNames := TStringList.Create();
|
|
ParamConstraints := TStringList.Create();
|
|
IntfDef := TInterfaceTypeDef.Create();
|
|
end;
|
|
|
|
destructor TGenericInterfaceDef.Destroy;
|
|
begin
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TGenericRecordDef }
|
|
|
|
constructor TGenericRecordDef.Create;
|
|
begin
|
|
inherited Create();
|
|
ParamNames := TStringList.Create();
|
|
ParamConstraints := TStringList.Create();
|
|
RecordDef := TRecordTypeDef.Create();
|
|
end;
|
|
|
|
destructor TGenericRecordDef.Destroy;
|
|
begin
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TGenericRecordInstance }
|
|
|
|
constructor TGenericRecordInstance.Create;
|
|
begin
|
|
inherited Create();
|
|
RecordDef := TRecordTypeDef.Create();
|
|
end;
|
|
|
|
destructor TGenericRecordInstance.Destroy;
|
|
begin
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TGenericInterfaceInstance }
|
|
|
|
constructor TGenericInterfaceInstance.Create;
|
|
begin
|
|
inherited Create();
|
|
IntfDef := TInterfaceTypeDef.Create();
|
|
end;
|
|
|
|
destructor TGenericInterfaceInstance.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TGenericInstance }
|
|
|
|
constructor TGenericInstance.Create;
|
|
begin
|
|
inherited Create();
|
|
ClassDef := TClassTypeDef.Create();
|
|
end;
|
|
|
|
destructor TGenericInstance.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TTypeDecl }
|
|
|
|
destructor TTypeDecl.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TBlock }
|
|
|
|
constructor TBlock.Create;
|
|
begin
|
|
inherited Create();
|
|
TypeDecls := TObjectList.Create(True);
|
|
ConstDecls := TObjectList.Create(True);
|
|
Decls := TObjectList.Create(True);
|
|
ProcDecls := TObjectList.Create(True);
|
|
Stmts := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TBlock.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TEnumTypeDef }
|
|
|
|
constructor TEnumTypeDef.Create;
|
|
begin
|
|
inherited Create();
|
|
Members := TStringList.Create();
|
|
end;
|
|
|
|
destructor TEnumTypeDef.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
procedure TEnumTypeDef.AddMember(const AName: string; AValue: Integer);
|
|
begin
|
|
Members.AddObject(AName, TObject(Pointer(PtrUInt(AValue))));
|
|
end;
|
|
|
|
function TEnumTypeDef.OrdinalAt(AIndex: Integer): Integer;
|
|
begin
|
|
Result := Integer(PtrUInt(Pointer(Members.Objects[AIndex])));
|
|
end;
|
|
|
|
{ TCaseBranch }
|
|
|
|
constructor TCaseBranch.Create;
|
|
begin
|
|
inherited Create();
|
|
Values := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TCaseBranch.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TCaseStmt }
|
|
|
|
constructor TCaseStmt.Create;
|
|
begin
|
|
inherited Create();
|
|
Branches := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TCaseStmt.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TProgram }
|
|
|
|
constructor TProgram.Create;
|
|
begin
|
|
inherited Create();
|
|
UsedUnits := TStringList.Create();
|
|
GenericInstances := TObjectList.Create(True);
|
|
GenericFuncInstances := TObjectList.Create(True);
|
|
GenericMethodInstances := TObjectList.Create(True);
|
|
GenericIntfInstances := TObjectList.Create(True);
|
|
GenericRecordInstances := TObjectList.Create(True);
|
|
LinkLibs := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TConstDecl.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
destructor TProgram.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ TUnit }
|
|
|
|
constructor TUnit.Create;
|
|
begin
|
|
inherited Create();
|
|
UsedUnits := TStringList.Create();
|
|
ImplUsedUnits := TStringList.Create();
|
|
IntfBlock := TBlock.Create();
|
|
ImplBlock := TBlock.Create();
|
|
InitStmts := nil;
|
|
FinalStmts := nil;
|
|
GenericInstances := TObjectList.Create(True);
|
|
GenericFuncInstances := TObjectList.Create(True);
|
|
GenericMethodInstances := TObjectList.Create(True);
|
|
GenericIntfInstances := TObjectList.Create(True);
|
|
GenericRecordInstances := TObjectList.Create(True);
|
|
LinkLibs := TObjectList.Create(True);
|
|
end;
|
|
|
|
destructor TUnit.Destroy;
|
|
begin
|
|
{ Owned class fields released by ARC field cleanup. }
|
|
inherited Destroy();
|
|
end;
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
{ Deep clone for generic-instance method body re-analysis }
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
function CloneVarDecl(ASrc: TVarDecl): TVarDecl; forward;
|
|
function CloneFieldDecl(ASrc: TFieldDecl): TFieldDecl; forward;
|
|
function ClonePropertyDecl(ASrc: TPropertyDecl): TPropertyDecl; forward;
|
|
{ CloneTypeDecl, CloneConstDecl, CloneMethodDecl, CloneMethodParam,
|
|
CloneTypeDef are now declared in the interface section. }
|
|
function CloneGenericTypeDef(ASrc: TGenericTypeDef): TGenericTypeDef; forward;
|
|
function CloneInterfaceTypeDef(ASrc: TInterfaceTypeDef): TInterfaceTypeDef; forward;
|
|
function CloneGenericInterfaceDef(ASrc: TGenericInterfaceDef): TGenericInterfaceDef; forward;
|
|
function CloneProceduralTypeDef(ASrc: TProceduralTypeDef): TProceduralTypeDef; forward;
|
|
function CloneExceptHandler(ASrc: TExceptHandlerClause): TExceptHandlerClause; forward;
|
|
function CloneCaseBranch(ASrc: TCaseBranch): TCaseBranch; forward;
|
|
function CloneCompound(ASrc: TCompoundStmt): TCompoundStmt; forward;
|
|
|
|
procedure CopyExprPos(ADst, ASrc: TASTExpr);
|
|
begin
|
|
ADst.Line := ASrc.Line;
|
|
ADst.Col := ASrc.Col;
|
|
end;
|
|
|
|
procedure CopyStmtPos(ADst, ASrc: TASTStmt);
|
|
begin
|
|
ADst.Line := ASrc.Line;
|
|
ADst.Col := ASrc.Col;
|
|
end;
|
|
|
|
function CloneExprList(ASrc: TObjectList): TObjectList;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := TObjectList.Create(True);
|
|
for I := 0 to ASrc.Count - 1 do
|
|
Result.Add(CloneExpr(TASTExpr(ASrc.Items[I])));
|
|
end;
|
|
|
|
function CloneExpr(AExpr: TASTExpr): TASTExpr;
|
|
var
|
|
IL: TIntLiteral;
|
|
FL: TFloatLiteral;
|
|
SL: TStringLiteral;
|
|
SS: TStringSubscriptExpr;
|
|
AL: TArrayLiteralExpr;
|
|
SRE: TSetRangeExpr;
|
|
IE: TIdentExpr;
|
|
FA: TFieldAccessExpr;
|
|
IsE: TIsExpr;
|
|
InhE: TInheritedCallExpr;
|
|
CloneI: Integer;
|
|
AsE: TAsExpr;
|
|
SuE: TSupportsExpr;
|
|
BE: TBinaryExpr;
|
|
NE: TNotExpr;
|
|
FCE: TFuncCallExpr;
|
|
DE: TDerefExpr;
|
|
AoE: TAddrOfExpr;
|
|
MCE: TMethodCallExpr;
|
|
I: Integer;
|
|
begin
|
|
if AExpr = nil then
|
|
begin Result := nil; Exit; end;
|
|
|
|
if AExpr is TIntLiteral then
|
|
begin
|
|
IL := TIntLiteral.Create();
|
|
IL.Value := TIntLiteral(AExpr).Value;
|
|
Result := IL;
|
|
end
|
|
else if AExpr is TFloatLiteral then
|
|
begin
|
|
FL := TFloatLiteral.Create();
|
|
FL.Value := TFloatLiteral(AExpr).Value;
|
|
Result := FL;
|
|
end
|
|
else if AExpr is TStringLiteral then
|
|
begin
|
|
SL := TStringLiteral.Create();
|
|
SL.Value := TStringLiteral(AExpr).Value;
|
|
Result := SL;
|
|
end
|
|
else if AExpr is TNilLiteral then
|
|
begin
|
|
Result := TNilLiteral.Create();
|
|
end
|
|
else if AExpr is TStringSubscriptExpr then
|
|
begin
|
|
SS := TStringSubscriptExpr.Create();
|
|
SS.StrExpr := CloneExpr(TStringSubscriptExpr(AExpr).StrExpr);
|
|
SS.IndexExpr := CloneExpr(TStringSubscriptExpr(AExpr).IndexExpr);
|
|
Result := SS;
|
|
end
|
|
else if AExpr is TArrayLiteralExpr then
|
|
begin
|
|
AL := TArrayLiteralExpr.Create();
|
|
for I := 0 to TArrayLiteralExpr(AExpr).Elements.Count - 1 do
|
|
AL.Elements.Add(CloneExpr(TASTExpr(TArrayLiteralExpr(AExpr).Elements.Items[I])));
|
|
AL.IsConstArray := TArrayLiteralExpr(AExpr).IsConstArray;
|
|
Result := AL;
|
|
end
|
|
else if AExpr is TSetRangeExpr then
|
|
begin
|
|
SRE := TSetRangeExpr.Create();
|
|
SRE.LowExpr := CloneExpr(TSetRangeExpr(AExpr).LowExpr);
|
|
SRE.HighExpr := CloneExpr(TSetRangeExpr(AExpr).HighExpr);
|
|
Result := SRE;
|
|
end
|
|
else if AExpr is TIdentExpr then
|
|
begin
|
|
IE := TIdentExpr.Create();
|
|
IE.Name := TIdentExpr(AExpr).Name;
|
|
{ Note: leave IsConstant/ParamMode/IsImplicitSelf/etc. nil/false —
|
|
semantic analyser must re-resolve in the new instance's scope. }
|
|
Result := IE;
|
|
end
|
|
else if AExpr is TFieldAccessExpr then
|
|
begin
|
|
FA := TFieldAccessExpr.Create();
|
|
FA.RecordName := TFieldAccessExpr(AExpr).RecordName;
|
|
FA.FieldName := TFieldAccessExpr(AExpr).FieldName;
|
|
FA.Base := CloneExpr(TFieldAccessExpr(AExpr).Base);
|
|
FA.PropIndexExpr := CloneExpr(TFieldAccessExpr(AExpr).PropIndexExpr);
|
|
Result := FA;
|
|
end
|
|
else if AExpr is TIsExpr then
|
|
begin
|
|
IsE := TIsExpr.Create();
|
|
IsE.Obj := CloneExpr(TIsExpr(AExpr).Obj);
|
|
IsE.TypeName := TIsExpr(AExpr).TypeName;
|
|
Result := IsE;
|
|
end
|
|
else if AExpr is TInheritedCallExpr then
|
|
begin
|
|
InhE := TInheritedCallExpr.Create();
|
|
InhE.Name := TInheritedCallExpr(AExpr).Name;
|
|
for CloneI := 0 to TInheritedCallExpr(AExpr).Args.Count - 1 do
|
|
InhE.Args.Add(CloneExpr(TASTExpr(TInheritedCallExpr(AExpr).Args.Items[CloneI])));
|
|
Result := InhE;
|
|
end
|
|
else if AExpr is TAsExpr then
|
|
begin
|
|
AsE := TAsExpr.Create();
|
|
AsE.Obj := CloneExpr(TAsExpr(AExpr).Obj);
|
|
AsE.TypeName := TAsExpr(AExpr).TypeName;
|
|
Result := AsE;
|
|
end
|
|
else if AExpr is TSupportsExpr then
|
|
begin
|
|
SuE := TSupportsExpr.Create();
|
|
SuE.Obj := CloneExpr(TSupportsExpr(AExpr).Obj);
|
|
SuE.IntfTypeName := TSupportsExpr(AExpr).IntfTypeName;
|
|
SuE.OutVarName := TSupportsExpr(AExpr).OutVarName;
|
|
Result := SuE;
|
|
end
|
|
else if AExpr is TBinaryExpr then
|
|
begin
|
|
BE := TBinaryExpr.Create();
|
|
BE.Op := TBinaryExpr(AExpr).Op;
|
|
BE.Left := CloneExpr(TBinaryExpr(AExpr).Left);
|
|
BE.Right := CloneExpr(TBinaryExpr(AExpr).Right);
|
|
Result := BE;
|
|
end
|
|
else if AExpr is TNotExpr then
|
|
begin
|
|
NE := TNotExpr.Create();
|
|
NE.Expr := CloneExpr(TNotExpr(AExpr).Expr);
|
|
Result := NE;
|
|
end
|
|
else if AExpr is TFuncCallExpr then
|
|
begin
|
|
FCE := TFuncCallExpr.Create();
|
|
FCE.Name := TFuncCallExpr(AExpr).Name;
|
|
for I := 0 to TFuncCallExpr(AExpr).Args.Count - 1 do
|
|
FCE.Args.Add(CloneExpr(TASTExpr(TFuncCallExpr(AExpr).Args.Items[I])));
|
|
Result := FCE;
|
|
end
|
|
else if AExpr is TDerefExpr then
|
|
begin
|
|
DE := TDerefExpr.Create();
|
|
DE.Expr := CloneExpr(TDerefExpr(AExpr).Expr);
|
|
Result := DE;
|
|
end
|
|
else if AExpr is TAddrOfExpr then
|
|
begin
|
|
AoE := TAddrOfExpr.Create();
|
|
AoE.Expr := CloneExpr(TAddrOfExpr(AExpr).Expr);
|
|
Result := AoE;
|
|
end
|
|
else if AExpr is TMethodCallExpr then
|
|
begin
|
|
MCE := TMethodCallExpr.Create();
|
|
MCE.ObjectName := TMethodCallExpr(AExpr).ObjectName;
|
|
MCE.Name := TMethodCallExpr(AExpr).Name;
|
|
MCE.ObjExpr := CloneExpr(TMethodCallExpr(AExpr).ObjExpr);
|
|
for I := 0 to TMethodCallExpr(AExpr).Args.Count - 1 do
|
|
MCE.Args.Add(CloneExpr(TASTExpr(TMethodCallExpr(AExpr).Args.Items[I])));
|
|
Result := MCE;
|
|
end
|
|
else
|
|
raise Exception.CreateFmt('CloneExpr: unhandled expression node %s',
|
|
[AExpr.ClassName]);
|
|
|
|
CopyExprPos(Result, AExpr);
|
|
end;
|
|
|
|
function CloneCompound(ASrc: TCompoundStmt): TCompoundStmt;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if ASrc = nil then
|
|
begin Result := nil; Exit; end;
|
|
Result := TCompoundStmt.Create();
|
|
CopyStmtPos(Result, ASrc);
|
|
for I := 0 to ASrc.Stmts.Count - 1 do
|
|
Result.Stmts.Add(CloneStmt(TASTStmt(ASrc.Stmts.Items[I])));
|
|
end;
|
|
|
|
function CloneExceptHandler(ASrc: TExceptHandlerClause): TExceptHandlerClause;
|
|
begin
|
|
Result := TExceptHandlerClause.Create();
|
|
Result.VarName := ASrc.VarName;
|
|
Result.TypeName := ASrc.TypeName;
|
|
Result.Body := CloneCompound(ASrc.Body);
|
|
end;
|
|
|
|
function CloneCaseBranch(ASrc: TCaseBranch): TCaseBranch;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := TCaseBranch.Create();
|
|
for I := 0 to ASrc.Values.Count - 1 do
|
|
Result.Values.Add(CloneExpr(TASTExpr(ASrc.Values.Items[I])));
|
|
Result.Stmt := CloneStmt(ASrc.Stmt);
|
|
end;
|
|
|
|
function CloneStmt(AStmt: TASTStmt): TASTStmt;
|
|
var
|
|
AS_: TAssignment;
|
|
IF_: TIfStmt;
|
|
CS_: TCompoundStmt;
|
|
WS_: TWhileStmt;
|
|
RS_: TRepeatStmt;
|
|
FS_: TForStmt;
|
|
FIS_: TForInStmt;
|
|
TFS_: TTryFinallyStmt;
|
|
TES_: TTryExceptStmt;
|
|
RaS_: TRaiseStmt;
|
|
ExS_: TExitStmt;
|
|
CSS_: TCaseStmt;
|
|
FAS_: TFieldAssignment;
|
|
SSA_: TStaticSubscriptAssign;
|
|
PWS_: TPointerWriteStmt;
|
|
PC_: TProcCall;
|
|
MCS_: TMethodCallStmt;
|
|
ICS_: TInheritedCallStmt;
|
|
I: Integer;
|
|
begin
|
|
if AStmt = nil then
|
|
begin Result := nil; Exit; end;
|
|
|
|
if AStmt is TAssignment then
|
|
begin
|
|
AS_ := TAssignment.Create();
|
|
AS_.Name := TAssignment(AStmt).Name;
|
|
AS_.Expr := CloneExpr(TAssignment(AStmt).Expr);
|
|
Result := AS_;
|
|
end
|
|
else if AStmt is TIfStmt then
|
|
begin
|
|
IF_ := TIfStmt.Create();
|
|
IF_.Condition := CloneExpr(TIfStmt(AStmt).Condition);
|
|
IF_.ThenStmt := CloneStmt(TIfStmt(AStmt).ThenStmt);
|
|
IF_.ElseStmt := CloneStmt(TIfStmt(AStmt).ElseStmt);
|
|
Result := IF_;
|
|
end
|
|
else if AStmt is TCompoundStmt then
|
|
begin
|
|
CS_ := TCompoundStmt.Create();
|
|
for I := 0 to TCompoundStmt(AStmt).Stmts.Count - 1 do
|
|
CS_.Stmts.Add(CloneStmt(TASTStmt(TCompoundStmt(AStmt).Stmts.Items[I])));
|
|
Result := CS_;
|
|
end
|
|
else if AStmt is TWhileStmt then
|
|
begin
|
|
WS_ := TWhileStmt.Create();
|
|
WS_.Condition := CloneExpr(TWhileStmt(AStmt).Condition);
|
|
WS_.Body := CloneStmt(TWhileStmt(AStmt).Body);
|
|
Result := WS_;
|
|
end
|
|
else if AStmt is TRepeatStmt then
|
|
begin
|
|
RS_ := TRepeatStmt.Create();
|
|
RS_.Body := CloneCompound(TRepeatStmt(AStmt).Body);
|
|
RS_.Condition := CloneExpr(TRepeatStmt(AStmt).Condition);
|
|
Result := RS_;
|
|
end
|
|
else if AStmt is TForStmt then
|
|
begin
|
|
FS_ := TForStmt.Create();
|
|
FS_.VarName := TForStmt(AStmt).VarName;
|
|
FS_.StartExpr := CloneExpr(TForStmt(AStmt).StartExpr);
|
|
FS_.EndExpr := CloneExpr(TForStmt(AStmt).EndExpr);
|
|
FS_.IsDownTo := TForStmt(AStmt).IsDownTo;
|
|
FS_.Body := CloneStmt(TForStmt(AStmt).Body);
|
|
Result := FS_;
|
|
end
|
|
else if AStmt is TForInStmt then
|
|
begin
|
|
FIS_ := TForInStmt.Create();
|
|
FIS_.VarName := TForInStmt(AStmt).VarName;
|
|
FIS_.CollExpr := CloneExpr(TForInStmt(AStmt).CollExpr);
|
|
FIS_.Body := CloneStmt(TForInStmt(AStmt).Body);
|
|
Result := FIS_;
|
|
end
|
|
else if AStmt is TTryFinallyStmt then
|
|
begin
|
|
TFS_ := TTryFinallyStmt.Create();
|
|
TFS_.TryBody := CloneCompound(TTryFinallyStmt(AStmt).TryBody);
|
|
TFS_.FinallyBody := CloneCompound(TTryFinallyStmt(AStmt).FinallyBody);
|
|
Result := TFS_;
|
|
end
|
|
else if AStmt is TTryExceptStmt then
|
|
begin
|
|
TES_ := TTryExceptStmt.Create();
|
|
TES_.TryBody := CloneCompound(TTryExceptStmt(AStmt).TryBody);
|
|
for I := 0 to TTryExceptStmt(AStmt).Handlers.Count - 1 do
|
|
TES_.Handlers.Add(
|
|
CloneExceptHandler(
|
|
TExceptHandlerClause(TTryExceptStmt(AStmt).Handlers.Items[I])));
|
|
TES_.ElseBody := CloneCompound(TTryExceptStmt(AStmt).ElseBody);
|
|
TES_.ExceptBody := CloneCompound(TTryExceptStmt(AStmt).ExceptBody);
|
|
Result := TES_;
|
|
end
|
|
else if AStmt is TRaiseStmt then
|
|
begin
|
|
RaS_ := TRaiseStmt.Create();
|
|
RaS_.Expr := CloneExpr(TRaiseStmt(AStmt).Expr);
|
|
Result := RaS_;
|
|
end
|
|
else if AStmt is TExitStmt then
|
|
begin
|
|
ExS_ := TExitStmt.Create();
|
|
ExS_.Value := CloneExpr(TExitStmt(AStmt).Value);
|
|
Result := ExS_;
|
|
end
|
|
else if AStmt is TBreakStmt then
|
|
Result := TBreakStmt.Create()
|
|
else if AStmt is TContinueStmt then
|
|
Result := TContinueStmt.Create()
|
|
else if AStmt is TCaseStmt then
|
|
begin
|
|
CSS_ := TCaseStmt.Create();
|
|
CSS_.Selector := CloneExpr(TCaseStmt(AStmt).Selector);
|
|
for I := 0 to TCaseStmt(AStmt).Branches.Count - 1 do
|
|
CSS_.Branches.Add(
|
|
CloneCaseBranch(TCaseBranch(TCaseStmt(AStmt).Branches.Items[I])));
|
|
CSS_.ElseStmt := CloneStmt(TCaseStmt(AStmt).ElseStmt);
|
|
Result := CSS_;
|
|
end
|
|
else if AStmt is TFieldAssignment then
|
|
begin
|
|
FAS_ := TFieldAssignment.Create();
|
|
FAS_.RecordName := TFieldAssignment(AStmt).RecordName;
|
|
FAS_.FieldName := TFieldAssignment(AStmt).FieldName;
|
|
FAS_.Expr := CloneExpr(TFieldAssignment(AStmt).Expr);
|
|
FAS_.ObjExpr := CloneExpr(TFieldAssignment(AStmt).ObjExpr);
|
|
FAS_.PropIndexExpr := CloneExpr(TFieldAssignment(AStmt).PropIndexExpr);
|
|
Result := FAS_;
|
|
end
|
|
else if AStmt is TStaticSubscriptAssign then
|
|
begin
|
|
SSA_ := TStaticSubscriptAssign.Create();
|
|
SSA_.ArrayName := TStaticSubscriptAssign(AStmt).ArrayName;
|
|
SSA_.IndexExpr := CloneExpr(TStaticSubscriptAssign(AStmt).IndexExpr);
|
|
SSA_.ValueExpr := CloneExpr(TStaticSubscriptAssign(AStmt).ValueExpr);
|
|
SSA_.BaseExpr := CloneExpr(TStaticSubscriptAssign(AStmt).BaseExpr);
|
|
Result := SSA_;
|
|
end
|
|
else if AStmt is TPointerWriteStmt then
|
|
begin
|
|
PWS_ := TPointerWriteStmt.Create();
|
|
PWS_.PtrExpr := CloneExpr(TPointerWriteStmt(AStmt).PtrExpr);
|
|
PWS_.ValExpr := CloneExpr(TPointerWriteStmt(AStmt).ValExpr);
|
|
Result := PWS_;
|
|
end
|
|
else if AStmt is TProcCall then
|
|
begin
|
|
PC_ := TProcCall.Create();
|
|
PC_.Name := TProcCall(AStmt).Name;
|
|
for I := 0 to TProcCall(AStmt).Args.Count - 1 do
|
|
PC_.Args.Add(CloneExpr(TASTExpr(TProcCall(AStmt).Args.Items[I])));
|
|
Result := PC_;
|
|
end
|
|
else if AStmt is TMethodCallStmt then
|
|
begin
|
|
MCS_ := TMethodCallStmt.Create();
|
|
MCS_.ObjectName := TMethodCallStmt(AStmt).ObjectName;
|
|
MCS_.Name := TMethodCallStmt(AStmt).Name;
|
|
MCS_.ObjExpr := CloneExpr(TMethodCallStmt(AStmt).ObjExpr);
|
|
for I := 0 to TMethodCallStmt(AStmt).Args.Count - 1 do
|
|
MCS_.Args.Add(CloneExpr(TASTExpr(TMethodCallStmt(AStmt).Args.Items[I])));
|
|
Result := MCS_;
|
|
end
|
|
else if AStmt is TInheritedCallStmt then
|
|
begin
|
|
ICS_ := TInheritedCallStmt.Create();
|
|
ICS_.Name := TInheritedCallStmt(AStmt).Name;
|
|
for I := 0 to TInheritedCallStmt(AStmt).Args.Count - 1 do
|
|
ICS_.Args.Add(CloneExpr(TASTExpr(TInheritedCallStmt(AStmt).Args.Items[I])));
|
|
Result := ICS_;
|
|
end
|
|
else
|
|
raise Exception.CreateFmt('CloneStmt: unhandled statement node %s',
|
|
[AStmt.ClassName]);
|
|
|
|
CopyStmtPos(Result, AStmt);
|
|
end;
|
|
|
|
function CloneVarDecl(ASrc: TVarDecl): TVarDecl;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := TVarDecl.Create();
|
|
Result.Line := ASrc.Line;
|
|
Result.Col := ASrc.Col;
|
|
Result.TypeName := ASrc.TypeName;
|
|
for I := 0 to ASrc.Names.Count - 1 do
|
|
Result.Names.Add(ASrc.Names.Strings[I]);
|
|
for I := 0 to ASrc.Attributes.Count - 1 do
|
|
Result.Attributes.Add(ASrc.Attributes.Strings[I]);
|
|
if ASrc.InitConst <> nil then
|
|
Result.InitConst := CloneConstDecl(ASrc.InitConst);
|
|
end;
|
|
|
|
function CloneFieldDecl(ASrc: TFieldDecl): TFieldDecl;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := TFieldDecl.Create();
|
|
Result.Line := ASrc.Line;
|
|
Result.Col := ASrc.Col;
|
|
Result.TypeName := ASrc.TypeName;
|
|
for I := 0 to ASrc.Names.Count - 1 do
|
|
Result.Names.Add(ASrc.Names.Strings[I]);
|
|
for I := 0 to ASrc.Attributes.Count - 1 do
|
|
Result.Attributes.Add(ASrc.Attributes.Strings[I]);
|
|
{ Carry the resolved semantic flags through the export clone. Without
|
|
these, the .bif encoder sees defaults: a [Weak]/[Unretained] field
|
|
round-trips as a plain owning field, and a `static var` loses both its
|
|
class-var marker and its mangled emit label — so a cross-unit consumer
|
|
cannot register the shared global. }
|
|
Result.IsWeak := ASrc.IsWeak;
|
|
Result.IsUnretained := ASrc.IsUnretained;
|
|
Result.IsClassVar := ASrc.IsClassVar;
|
|
Result.ClassVarEmitName := ASrc.ClassVarEmitName;
|
|
{ Visibility must survive the export clone or a cross-unit consumer would see
|
|
every imported field as public and enforcement would silently disappear at
|
|
the unit boundary. }
|
|
Result.Visibility := ASrc.Visibility;
|
|
end;
|
|
|
|
function ClonePropertyDecl(ASrc: TPropertyDecl): TPropertyDecl;
|
|
begin
|
|
Result := TPropertyDecl.Create();
|
|
Result.Name := ASrc.Name;
|
|
Result.TypeName := ASrc.TypeName;
|
|
Result.ReadName := ASrc.ReadName;
|
|
Result.WriteName := ASrc.WriteName;
|
|
Result.IndexParamName := ASrc.IndexParamName;
|
|
Result.IndexTypeName := ASrc.IndexTypeName;
|
|
{ IsDefault round-trips the `default` directive; IsStatic marks a static
|
|
property whose accessors take no Self. Both were silently dropped. }
|
|
Result.IsDefault := ASrc.IsDefault;
|
|
Result.IsStatic := ASrc.IsStatic;
|
|
Result.Visibility := ASrc.Visibility;
|
|
end;
|
|
|
|
function CloneTypeDecl(ASrc: TTypeDecl): TTypeDecl;
|
|
begin
|
|
Result := TTypeDecl.Create();
|
|
Result.Line := ASrc.Line;
|
|
Result.Col := ASrc.Col;
|
|
Result.Name := ASrc.Name;
|
|
Result.Def := CloneTypeDef(ASrc.Def);
|
|
end;
|
|
|
|
function CloneConstDecl(ASrc: TConstDecl): TConstDecl;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := TConstDecl.Create();
|
|
Result.Line := ASrc.Line;
|
|
Result.Col := ASrc.Col;
|
|
Result.Name := ASrc.Name;
|
|
Result.TypeName := ASrc.TypeName;
|
|
Result.IntVal := ASrc.IntVal;
|
|
Result.StrVal := ASrc.StrVal;
|
|
Result.IsString := ASrc.IsString;
|
|
Result.IsFloat := ASrc.IsFloat;
|
|
if ASrc.ConstParts <> nil then
|
|
begin
|
|
Result.ConstParts := TStringList.Create();
|
|
for I := 0 to ASrc.ConstParts.Count - 1 do
|
|
Result.ConstParts.AddObject(
|
|
ASrc.ConstParts.Strings[I], ASrc.ConstParts.Objects[I]);
|
|
end;
|
|
Result.IsArrayConst := ASrc.IsArrayConst;
|
|
Result.ArrayIndexType := ASrc.ArrayIndexType;
|
|
Result.ArrayElemType := ASrc.ArrayElemType;
|
|
Result.ArrayIsRangeIndexed := ASrc.ArrayIsRangeIndexed;
|
|
Result.ArrayLowBound := ASrc.ArrayLowBound;
|
|
Result.ArrayHighBound := ASrc.ArrayHighBound;
|
|
if ASrc.ArrayDimLows <> nil then
|
|
begin
|
|
Result.ArrayDimLows := TStringList.Create();
|
|
for I := 0 to ASrc.ArrayDimLows.Count - 1 do
|
|
Result.ArrayDimLows.Add(ASrc.ArrayDimLows.Strings[I]);
|
|
end;
|
|
if ASrc.ArrayDimHighs <> nil then
|
|
begin
|
|
Result.ArrayDimHighs := TStringList.Create();
|
|
for I := 0 to ASrc.ArrayDimHighs.Count - 1 do
|
|
Result.ArrayDimHighs.Add(ASrc.ArrayDimHighs.Strings[I]);
|
|
end;
|
|
if ASrc.ArrayElements <> nil then
|
|
begin
|
|
Result.ArrayElements := TStringList.Create();
|
|
for I := 0 to ASrc.ArrayElements.Count - 1 do
|
|
Result.ArrayElements.Add(ASrc.ArrayElements.Strings[I]);
|
|
end;
|
|
if ASrc.IntExprTokens <> nil then
|
|
begin
|
|
Result.IntExprTokens := TStringList.Create();
|
|
for I := 0 to ASrc.IntExprTokens.Count - 1 do
|
|
Result.IntExprTokens.AddObject(
|
|
ASrc.IntExprTokens.Strings[I], ASrc.IntExprTokens.Objects[I]);
|
|
end;
|
|
if ASrc.IntValueExpr <> nil then
|
|
Result.IntValueExpr := CloneExpr(ASrc.IntValueExpr);
|
|
Result.IsSet := ASrc.IsSet;
|
|
if ASrc.SetElements <> nil then
|
|
begin
|
|
Result.SetElements := TStringList.Create();
|
|
for I := 0 to ASrc.SetElements.Count - 1 do
|
|
Result.SetElements.Add(ASrc.SetElements.Strings[I]);
|
|
end;
|
|
if ASrc.ConstSetBytes <> nil then
|
|
begin
|
|
Result.ConstSetBytes := TStringList.Create();
|
|
for I := 0 to ASrc.ConstSetBytes.Count - 1 do
|
|
Result.ConstSetBytes.Add(ASrc.ConstSetBytes.Strings[I]);
|
|
end;
|
|
Result.ResolvedSetQbeName := ASrc.ResolvedSetQbeName;
|
|
end;
|
|
|
|
function CloneMethodParam(ASrc: TMethodParam): TMethodParam;
|
|
begin
|
|
Result := TMethodParam.Create();
|
|
Result.Line := ASrc.Line;
|
|
Result.Col := ASrc.Col;
|
|
Result.ParamName := ASrc.ParamName;
|
|
Result.TypeName := ASrc.TypeName;
|
|
Result.IsVarParam := ASrc.IsVarParam;
|
|
Result.IsConstParam := ASrc.IsConstParam;
|
|
Result.IsOutParam := ASrc.IsOutParam;
|
|
Result.IsOpenArray := ASrc.IsOpenArray;
|
|
Result.DefaultValue := CloneExpr(ASrc.DefaultValue);
|
|
Result.HasDefault := ASrc.HasDefault;
|
|
end;
|
|
|
|
function CloneMethodDecl(ASrc: TMethodDecl): TMethodDecl;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := TMethodDecl.Create();
|
|
Result.Line := ASrc.Line;
|
|
Result.Col := ASrc.Col;
|
|
Result.Name := ASrc.Name;
|
|
Result.OwnerTypeName := ASrc.OwnerTypeName;
|
|
Result.ReturnTypeName := ASrc.ReturnTypeName;
|
|
Result.IsVirtual := ASrc.IsVirtual;
|
|
Result.IsOverride := ASrc.IsOverride;
|
|
Result.IsAbstract := ASrc.IsAbstract;
|
|
Result.IsOverload := ASrc.IsOverload;
|
|
Result.IsPublished := ASrc.IsPublished;
|
|
Result.IsExternal := ASrc.IsExternal;
|
|
Result.ExternalName := ASrc.ExternalName;
|
|
Result.ExternalLib := ASrc.ExternalLib;
|
|
Result.CallingConv := ASrc.CallingConv;
|
|
Result.IsRecordMethod := ASrc.IsRecordMethod;
|
|
{ Static (class-level) methods take no implicit Self. Dropping this in the
|
|
export clone made every cross-unit static method look like a normal
|
|
instance method, so TypeName.StaticMethod() resolution failed. }
|
|
Result.IsStatic := ASrc.IsStatic;
|
|
Result.Visibility := ASrc.Visibility;
|
|
for I := 0 to ASrc.Params.Count - 1 do
|
|
Result.Params.Add(CloneMethodParam(TMethodParam(ASrc.Params.Items[I])));
|
|
if ASrc.TypeParams <> nil then
|
|
begin
|
|
Result.TypeParams := TStringList.Create();
|
|
for I := 0 to ASrc.TypeParams.Count - 1 do
|
|
Result.TypeParams.Add(ASrc.TypeParams.Strings[I]);
|
|
end;
|
|
if ASrc.TypeParamConstraints <> nil then
|
|
begin
|
|
Result.TypeParamConstraints := TStringList.Create();
|
|
for I := 0 to ASrc.TypeParamConstraints.Count - 1 do
|
|
Result.TypeParamConstraints.Add(ASrc.TypeParamConstraints.Strings[I]);
|
|
end;
|
|
if ASrc.OwnerTypeParams <> nil then
|
|
begin
|
|
Result.OwnerTypeParams := TStringList.Create();
|
|
for I := 0 to ASrc.OwnerTypeParams.Count - 1 do
|
|
Result.OwnerTypeParams.Add(ASrc.OwnerTypeParams.Strings[I]);
|
|
end;
|
|
if (ASrc.Body <> nil) and ASrc.OwnBody then
|
|
begin
|
|
Result.Body := CloneBlock(ASrc.Body);
|
|
Result.OwnBody := True;
|
|
end
|
|
else
|
|
begin
|
|
Result.Body := ASrc.Body;
|
|
Result.OwnBody := False;
|
|
end;
|
|
end;
|
|
|
|
function CloneTypeDef(ASrc: TASTTypeDef): TASTTypeDef;
|
|
var
|
|
TA: TTypeAliasDef;
|
|
ST: TSetTypeDef;
|
|
RT: TRecordTypeDef;
|
|
ET: TEnumTypeDef;
|
|
I: Integer;
|
|
begin
|
|
if ASrc = nil then
|
|
begin Result := nil; Exit; end;
|
|
if ASrc is TTypeAliasDef then
|
|
begin
|
|
TA := TTypeAliasDef.Create();
|
|
TA.TypeName := TTypeAliasDef(ASrc).TypeName;
|
|
Result := TA;
|
|
end
|
|
else if ASrc is TSetTypeDef then
|
|
begin
|
|
ST := TSetTypeDef.Create();
|
|
ST.BaseTypeName := TSetTypeDef(ASrc).BaseTypeName;
|
|
Result := ST;
|
|
end
|
|
else if ASrc is TEnumTypeDef then
|
|
begin
|
|
ET := TEnumTypeDef.Create();
|
|
for I := 0 to TEnumTypeDef(ASrc).Members.Count - 1 do
|
|
ET.Members.AddObject(
|
|
TEnumTypeDef(ASrc).Members.Strings[I],
|
|
TEnumTypeDef(ASrc).Members.Objects[I]);
|
|
Result := ET;
|
|
end
|
|
else if ASrc is TRecordTypeDef then
|
|
begin
|
|
RT := TRecordTypeDef.Create();
|
|
RT.IsPacked := TRecordTypeDef(ASrc).IsPacked;
|
|
for I := 0 to TRecordTypeDef(ASrc).Fields.Count - 1 do
|
|
RT.Fields.Add(CloneFieldDecl(TFieldDecl(TRecordTypeDef(ASrc).Fields.Items[I])));
|
|
for I := 0 to TRecordTypeDef(ASrc).Methods.Count - 1 do
|
|
RT.Methods.Add(CloneMethodDecl(TMethodDecl(TRecordTypeDef(ASrc).Methods.Items[I])));
|
|
Result := RT;
|
|
end
|
|
else if ASrc is TClassTypeDef then
|
|
begin
|
|
Result := CloneClassTypeDef(TClassTypeDef(ASrc));
|
|
end
|
|
else if ASrc is TGenericTypeDef then
|
|
begin
|
|
Result := CloneGenericTypeDef(TGenericTypeDef(ASrc));
|
|
end
|
|
else if ASrc is TGenericInterfaceDef then
|
|
begin
|
|
Result := CloneGenericInterfaceDef(TGenericInterfaceDef(ASrc));
|
|
end
|
|
else if ASrc is TInterfaceTypeDef then
|
|
begin
|
|
Result := CloneInterfaceTypeDef(TInterfaceTypeDef(ASrc));
|
|
end
|
|
else if ASrc is TProceduralTypeDef then
|
|
begin
|
|
Result := CloneProceduralTypeDef(TProceduralTypeDef(ASrc));
|
|
end
|
|
else
|
|
raise Exception.CreateFmt(
|
|
'CloneTypeDef: unsupported type def %s', [ASrc.ClassName]);
|
|
|
|
Result.Line := ASrc.Line;
|
|
Result.Col := ASrc.Col;
|
|
end;
|
|
|
|
function CloneClassTypeDef(ASrc: TClassTypeDef): TClassTypeDef;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if ASrc = nil then begin Result := nil; Exit; end;
|
|
Result := TClassTypeDef.Create();
|
|
Result.ParentName := ASrc.ParentName;
|
|
Result.IsForward := ASrc.IsForward;
|
|
for I := 0 to ASrc.ImplementsNames.Count - 1 do
|
|
Result.ImplementsNames.Add(ASrc.ImplementsNames.Strings[I]);
|
|
for I := 0 to ASrc.ConstDecls.Count - 1 do
|
|
Result.ConstDecls.Add(CloneConstDecl(TConstDecl(ASrc.ConstDecls.Items[I])));
|
|
for I := 0 to ASrc.Fields.Count - 1 do
|
|
Result.Fields.Add(CloneFieldDecl(TFieldDecl(ASrc.Fields.Items[I])));
|
|
for I := 0 to ASrc.Methods.Count - 1 do
|
|
Result.Methods.Add(CloneMethodDecl(TMethodDecl(ASrc.Methods.Items[I])));
|
|
for I := 0 to ASrc.Attributes.Count - 1 do
|
|
Result.Attributes.Add(ASrc.Attributes.Strings[I]);
|
|
for I := 0 to ASrc.Properties.Count - 1 do
|
|
Result.Properties.Add(ClonePropertyDecl(TPropertyDecl(ASrc.Properties.Items[I])));
|
|
end;
|
|
|
|
function CloneGenericTypeDef(ASrc: TGenericTypeDef): TGenericTypeDef;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if ASrc = nil then begin Result := nil; Exit; end;
|
|
Result := TGenericTypeDef.Create();
|
|
for I := 0 to ASrc.ParamNames.Count - 1 do
|
|
begin
|
|
Result.ParamNames.Add(ASrc.ParamNames.Strings[I]);
|
|
if I < ASrc.ParamConstraints.Count then
|
|
Result.ParamConstraints.Add(ASrc.ParamConstraints.Strings[I])
|
|
else
|
|
Result.ParamConstraints.Add('');
|
|
end;
|
|
{ Replace the autoctor's blank ClassDef with a real clone. }
|
|
Result.ClassDef.Free();
|
|
Result.ClassDef := CloneClassTypeDef(ASrc.ClassDef);
|
|
end;
|
|
|
|
function CloneInterfaceTypeDef(ASrc: TInterfaceTypeDef): TInterfaceTypeDef;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if ASrc = nil then begin Result := nil; Exit; end;
|
|
Result := TInterfaceTypeDef.Create();
|
|
Result.ParentName := ASrc.ParentName;
|
|
Result.IsForward := ASrc.IsForward;
|
|
for I := 0 to ASrc.Methods.Count - 1 do
|
|
Result.Methods.Add(CloneMethodDecl(TMethodDecl(ASrc.Methods.Items[I])));
|
|
for I := 0 to ASrc.Properties.Count - 1 do
|
|
Result.Properties.Add(ClonePropertyDecl(TPropertyDecl(ASrc.Properties.Items[I])));
|
|
end;
|
|
|
|
function CloneGenericInterfaceDef(ASrc: TGenericInterfaceDef): TGenericInterfaceDef;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if ASrc = nil then begin Result := nil; Exit; end;
|
|
Result := TGenericInterfaceDef.Create();
|
|
for I := 0 to ASrc.ParamNames.Count - 1 do
|
|
begin
|
|
Result.ParamNames.Add(ASrc.ParamNames.Strings[I]);
|
|
if I < ASrc.ParamConstraints.Count then
|
|
Result.ParamConstraints.Add(ASrc.ParamConstraints.Strings[I])
|
|
else
|
|
Result.ParamConstraints.Add('');
|
|
end;
|
|
Result.IntfDef.Free();
|
|
Result.IntfDef := CloneInterfaceTypeDef(ASrc.IntfDef);
|
|
end;
|
|
|
|
function CloneProceduralTypeDef(ASrc: TProceduralTypeDef): TProceduralTypeDef;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if ASrc = nil then begin Result := nil; Exit; end;
|
|
Result := TProceduralTypeDef.Create();
|
|
Result.ReturnTypeName := ASrc.ReturnTypeName;
|
|
Result.IsFunction := ASrc.IsFunction;
|
|
Result.IsMethodPtr := ASrc.IsMethodPtr;
|
|
for I := 0 to ASrc.Params.Count - 1 do
|
|
Result.Params.Add(CloneMethodParam(TMethodParam(ASrc.Params.Items[I])));
|
|
end;
|
|
|
|
function CloneBlock(ABlock: TBlock): TBlock;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
if ABlock = nil then
|
|
begin Result := nil; Exit; end;
|
|
Result := TBlock.Create();
|
|
Result.Line := ABlock.Line;
|
|
Result.Col := ABlock.Col;
|
|
for I := 0 to ABlock.TypeDecls.Count - 1 do
|
|
Result.TypeDecls.Add(CloneTypeDecl(TTypeDecl(ABlock.TypeDecls.Items[I])));
|
|
for I := 0 to ABlock.ConstDecls.Count - 1 do
|
|
Result.ConstDecls.Add(CloneConstDecl(TConstDecl(ABlock.ConstDecls.Items[I])));
|
|
for I := 0 to ABlock.Decls.Count - 1 do
|
|
Result.Decls.Add(CloneVarDecl(TVarDecl(ABlock.Decls.Items[I])));
|
|
for I := 0 to ABlock.ProcDecls.Count - 1 do
|
|
Result.ProcDecls.Add(CloneMethodDecl(TMethodDecl(ABlock.ProcDecls.Items[I])));
|
|
for I := 0 to ABlock.Stmts.Count - 1 do
|
|
Result.Stmts.Add(CloneStmt(TASTStmt(ABlock.Stmts.Items[I])));
|
|
end;
|
|
|
|
end.
|