source: tags/1.2.0/UTarget.pas

Last change on this file was 137, checked in by chronos, 3 years ago
  • Fixed: Wrong index variable used in inc/dec merge optimization.
  • Fixed: Reload output if program is reset.
File size: 15.9 KB
Line 
1unit UTarget;
2
3{$mode Delphi}{$H+}
4
5interface
6
7uses
8 Classes, SysUtils, StrUtils, Registry, URegistry, fgl, Dialogs,
9 FileUtil, Process, Menus, LazFileUtils;
10
11type
12 TCompilerOptimization = (coNone, coNormal);
13
14 TTargetCapability = (tcRun, tcPause, tcStop, tcStepOut, tcStepOver, tcStepInto,
15 tcRunToCursor, tcCompile, tcBreakpoint);
16 TTargetCapabilities = set of TTargetCapability;
17
18 TRunState = (rsStopped, rsPaused, rsRunning, rsCompiling);
19
20 TStepOperation = (soNormal, soStepIn, soStepOut);
21
22 TDebugStep = class
23 SourcePosition: Integer;
24 ProgramPosition: Integer;
25 TargetPosition: Integer;
26 Operation: TStepOperation;
27 end;
28
29 { TDebugSteps }
30
31 TDebugSteps = class(TFPGObjectList<TDebugStep>)
32 function SearchBySourcePos(Pos: Integer): TDebugStep;
33 function SearchByProgramPos(Pos: Integer): TDebugStep;
34 function SearchByTargetPos(Pos: Integer): TDebugStep;
35 function SearchIndexByProgramPos(Pos: Integer): Integer;
36 procedure AddStep(SourcePos, TargetPos: Integer; Operation: TStepOperation);
37 procedure UpdateTargetPos(OldProgramFrom, OldProgramTo, NewProgramFrom,
38 NewProgramTo: Integer; NewTarget: Integer = 0);
39 end;
40
41 TBreakPoint = class
42 TargetAddress: Integer;
43 System: Boolean;
44 end;
45
46 { TBreakPoints }
47
48 TBreakPoints = class(TFPGObjectList<TBreakPoint>)
49 procedure AddItem(TargetAddress: Integer);
50 procedure SetSystem(TargetAddress: Integer);
51 procedure AddSystem(TargetAddress: Integer);
52 procedure ClearSystem;
53 function SearchByTargetPos(Pos: Integer): TBreakPoint;
54 end;
55
56 TMessage = class
57 Text: string;
58 Position: TPoint;
59 end;
60
61 { TMessages }
62
63 TMessages = class(TFPGObjectList<TMessage>)
64 private
65 FOnChange: TNotifyEvent;
66 procedure DoChange;
67 public
68 procedure AddMessage(Text: string);
69 procedure AppendMessage(Text: string);
70 property OnChange: TNotifyEvent read FOnChange write FOnChange;
71 end;
72
73 TLogEvent = procedure (Lines: TStrings) of object;
74 TMessageEvent = procedure (Text: string) of object;
75
76 { TTarget }
77
78 TTarget = class
79 private
80 FOnLog: TLogEvent;
81 FOnMessage: TMessageEvent;
82 protected
83 FCompiledExtension: string;
84 FRunExtension: string;
85 FSourceExtension: string;
86 FName: string;
87 FImageIndex: Integer;
88 FCapabilities: TTargetCapabilities;
89 FCompiled: Boolean;
90 function SourceReadNext: Char;
91 protected
92 FSourceCode: string;
93 FTargetCode: string;
94 FTargetIndex: Integer;
95 Indent: Integer;
96 FState: TRunState;
97 FOnChangeState: TNotifyEvent;
98 procedure LoadProgram; virtual;
99 procedure SetSourceCode(AValue: string); virtual;
100 function GetTargetCode: string; virtual;
101 procedure AddLine(Text: string = '');
102 function LongFileName(FileName: string): string;
103 function GetExecutionPosition: Integer; virtual;
104 procedure SetState(AValue: TRunState); virtual;
105 public
106 ProgramName: string;
107 OptimizationLevel: TCompilerOptimization;
108 CompilerPath: string;
109 ExecutorPath: string;
110 ProjectFileName: string;
111 BreakPoints: TBreakPoints;
112 DebugSteps: TDebugSteps;
113 DebugEnabled: Boolean;
114 Selected: Boolean;
115 procedure SendMessage(Text: string);
116 constructor Create; virtual;
117 destructor Destroy; override;
118 procedure Reset; virtual;
119 procedure OptimizeSource; virtual;
120 procedure Compile; virtual;
121 procedure CompileToFile; virtual;
122 procedure RunFromFile; virtual;
123 procedure Run; virtual;
124 procedure Pause; virtual;
125 procedure Stop; virtual;
126 procedure StepOver; virtual;
127 procedure StepInto; virtual;
128 procedure StepOut; virtual;
129 procedure RunToCursor(Pos: Integer); virtual;
130 procedure LoadFromRegistry(Context: TRegistryContext); virtual;
131 procedure SaveToRegistry(Context: TRegistryContext); virtual;
132 property SourceExtension: string read FSourceExtension;
133 property RunExtension: string read FRunExtension;
134 property CompiledExtension: string read FCompiledExtension;
135 property Name: string read FName;
136 property ImageIndex: Integer read FImageIndex;
137 property State: TRunState read FState write SetState;
138 property SourceCode: string write SetSourceCode;
139 property TargetCode: string read GetTargetCode;
140 property Compiled: Boolean read FCompiled write FCompiled;
141 property ExecutionPosition: Integer read GetExecutionPosition;
142 property Capabilities: TTargetCapabilities read FCapabilities;
143 property OnChangeState: TNotifyEvent read FOnChangeState write FOnChangeState;
144 property OnLog: TLogEvent read FOnLog write FOnLog;
145 property OnMessage: TMessageEvent read FOnMessage write FOnMessage;
146 end;
147
148 { TTargets }
149
150 TTargets = class(TFPGObjectList<TTarget>)
151 procedure LoadFromRegistry(Context: TRegistryContext);
152 procedure SaveToRegistry(Context: TRegistryContext);
153 function FindByName(Name: string): TTarget;
154 procedure LoadToMenuItem(MenuItem: TMenuItem; Action: TNotifyEvent;
155 CurrentTarget: TTarget);
156 end;
157
158
159resourcestring
160 SCompilerNotFound = 'Compiler "%s" not found';
161 SExecutorNotFound = 'Executor "%s" not found';
162 SCompiledFileNotFound = 'Program compiled file "%s" not found';
163 SStopped = 'Stopped';
164 SPaused = 'Paused';
165 SRunning = 'Running';
166 SCompiling = 'Compiling';
167 SNone = 'None';
168 SNormal = 'Normal';
169 SProgramIndexError = 'Old program index higher than new (old: %d, new: %d)';
170 SFirstIndexHigherThanLastIndex = 'First index higher than last index';
171 SFirstIndexOutOfRange = 'First index out of range';
172 SLastIndexOutOfRange = 'Last index out of range';
173
174const
175 CompiledDir = 'compiled';
176
177var
178 RunStateText: array[TRunState] of string = (SStopped, SPaused, SRunning,
179 SCompiling);
180 CompilerOptimizationText: array[TCompilerOptimization] of string = (SNone, SNormal);
181
182procedure UpdateTranslation;
183
184
185implementation
186
187procedure UpdateTranslation;
188begin
189 RunStateText[rsStopped] := SStopped;
190 RunStateText[rsPaused] := SPaused;
191 RunStateText[rsRunning] := SRunning;
192 RunStateText[rsCompiling] := SCompiling;
193 CompilerOptimizationText[coNone] := SNone;
194 CompilerOptimizationText[coNormal] := SNormal;
195end;
196
197{ TMessages }
198
199procedure TMessages.DoChange;
200begin
201 if Assigned(FOnChange) then FOnChange(Self);
202end;
203
204procedure TMessages.AddMessage(Text: string);
205var
206 NewItem: TMessage;
207begin
208 NewItem := TMessage.Create;
209 NewItem.Text := Text;
210 Add(NewItem);
211 DoChange;
212end;
213
214procedure TMessages.AppendMessage(Text: string);
215begin
216 if Count > 0 then begin
217 Last.Text := Last.Text + Text;
218 DoChange;
219 end else AddMessage(Text);
220end;
221
222{ TBreakPoints }
223
224procedure TBreakPoints.AddItem(TargetAddress: Integer);
225var
226 NewItem: TBreakPoint;
227begin
228 NewItem := TBreakPoint.Create;
229 NewItem.TargetAddress := TargetAddress;
230 Add(NewItem);
231end;
232
233procedure TBreakPoints.SetSystem(TargetAddress: Integer);
234begin
235 ClearSystem;
236 AddSystem(TargetAddress);
237end;
238
239procedure TBreakPoints.AddSystem(TargetAddress: Integer);
240var
241 NewItem: TBreakPoint;
242begin
243 NewItem := TBreakPoint.Create;
244 NewItem.TargetAddress := TargetAddress;
245 NewItem.System := True;
246 Add(NewItem);
247end;
248
249procedure TBreakPoints.ClearSystem;
250var
251 I: Integer;
252begin
253 for I := Count - 1 downto 0 do
254 if Items[I].System then Delete(I);
255end;
256
257function TBreakPoints.SearchByTargetPos(Pos: Integer): TBreakPoint;
258var
259 I: Integer;
260begin
261 I := 0;
262 while (I < Count) and (Items[I].TargetAddress < Pos) do Inc(I);
263 if I < Count then Result := Items[I]
264 else Result := nil;
265end;
266
267{ TDebugSteps }
268
269function TDebugSteps.SearchBySourcePos(Pos: Integer
270 ): TDebugStep;
271var
272 I: Integer;
273begin
274 I := 0;
275 while (I < Count) and (Items[I].SourcePosition < Pos) do Inc(I);
276 if I < Count then Result := Items[I]
277 else Result := nil;
278end;
279
280function TDebugSteps.SearchByProgramPos(Pos: Integer): TDebugStep;
281var
282 I: Integer;
283begin
284 I := 0;
285 while (I < Count) and (Items[I].ProgramPosition < Pos) do Inc(I);
286 if I < Count then Result := Items[I]
287 else Result := nil;
288end;
289
290function TDebugSteps.SearchByTargetPos(Pos: Integer
291 ): TDebugStep;
292var
293 I: Integer;
294begin
295 I := 0;
296 while (I < Count) and (Items[I].TargetPosition < Pos) do Inc(I);
297 if I < Count then Result := Items[I]
298 else Result := nil;
299end;
300
301function TDebugSteps.SearchIndexByProgramPos(Pos: Integer): Integer;
302var
303 I: Integer;
304begin
305 I := 0;
306 while (I < Count) and (Items[I].ProgramPosition < Pos) do Inc(I);
307 if I < Count then Result := I
308 else Result := -1;
309end;
310
311procedure TDebugSteps.AddStep(SourcePos, TargetPos: Integer;
312 Operation: TStepOperation);
313var
314 NewItem: TDebugStep;
315begin
316 NewItem := TDebugStep.Create;
317 NewItem.SourcePosition := SourcePos;
318 NewItem.ProgramPosition := TargetPos;
319 NewItem.TargetPosition := TargetPos;
320 NewItem.Operation := Operation;
321 Add(NewItem);
322end;
323
324procedure TDebugSteps.UpdateTargetPos(OldProgramFrom, OldProgramTo, NewProgramFrom,
325 NewProgramTo: Integer; NewTarget: Integer = 0);
326var
327 I: Integer;
328 First: Integer;
329 Last: Integer;
330begin
331 First := SearchIndexByProgramPos(OldProgramFrom);
332 Last := SearchIndexByProgramPos(OldProgramTo);
333 if (First <> -1) and (Last <> -1) then begin
334 if First > Last then
335 raise Exception.Create(SFirstIndexHigherThanLastIndex);
336 if (First < 0) or (First >= Count) then
337 raise Exception.Create(SFirstIndexOutOfRange);
338 if (Last < 0) or (Last >= Count) then
339 raise Exception.Create(SLastIndexOutOfRange);
340 for I := Last downto First + 1 do Delete(I);
341
342 if NewProgramTo - NewProgramFrom = 0 then begin
343 Delete(First);
344 end else
345 if NewProgramTo > NewProgramFrom then begin
346 Items[First].ProgramPosition := NewProgramFrom;
347 Items[First].TargetPosition := NewTarget;
348 end else
349 raise Exception.Create(Format(SProgramIndexError, [SProgramIndexError, NewProgramTo]));
350 end;
351end;
352
353{ TTargets }
354
355procedure TTargets.LoadFromRegistry(Context: TRegistryContext);
356var
357 I: Integer;
358begin
359 with TRegistryEx.Create do
360 try
361 CurrentContext := Context;
362 for I := 0 to Count - 1 do
363 Items[I].LoadFromRegistry(TRegistryContext.Create(Context.RootKey, Context.Key + '\' + TTarget(Items[I]).Name));
364 finally
365 Free;
366 end;
367end;
368
369procedure TTargets.SaveToRegistry(Context: TRegistryContext);
370var
371 I: Integer;
372begin
373 with TRegistryEx.Create do
374 try
375 CurrentContext := Context;
376 for I := 0 to Count - 1 do
377 with Items[I] do
378 Items[I].SaveToRegistry(TRegistryContext.Create(Context.RootKey, Context.Key + '\' + TTarget(Items[I]).Name));
379 finally
380 Free;
381 end;
382end;
383
384function TTargets.FindByName(Name: string): TTarget;
385var
386 I: Integer;
387begin
388 I := 0;
389 while (I < Count) and (Items[I].Name <> Name) do Inc(I);
390 if I < Count then Result := Items[I]
391 else Result := nil;
392end;
393
394procedure TTargets.LoadToMenuItem(MenuItem: TMenuItem; Action: TNotifyEvent
395 ; CurrentTarget: TTarget);
396var
397 NewMenuItem: TMenuItem;
398 I: Integer;
399begin
400 if Assigned(MenuItem) then begin
401 while MenuItem.Count > Count do
402 MenuItem.Delete(MenuItem.Count - 1);
403 while MenuItem.Count < Count do begin
404 NewMenuItem := TMenuItem.Create(MenuItem);
405 MenuItem.Add(NewMenuItem);
406 end;
407 for I := 0 to Count - 1 do begin
408 MenuItem.Items[I].Caption := Items[I].Name;
409 MenuItem.Items[I].OnClick := Action;
410 MenuItem.Items[I].ImageIndex := Items[I].ImageIndex;
411 if Items[I] = CurrentTarget then MenuItem.Items[I].Checked := True;
412 end;
413 end;
414end;
415
416{ TTarget }
417
418function TTarget.GetTargetCode: string;
419begin
420 Result := FTargetCode;
421end;
422
423function TTarget.GetExecutionPosition: Integer;
424begin
425 Result := 0;
426end;
427
428procedure TTarget.SetState(AValue: TRunState);
429begin
430 if FState = AValue then Exit;
431 FState := AValue;
432end;
433
434procedure TTarget.SendMessage(Text: string);
435begin
436 if Assigned(FOnMessage) then
437 FOnMessage(Text);
438end;
439
440procedure TTarget.SetSourceCode(AValue: string);
441begin
442 FSourceCode := AValue;
443end;
444
445procedure TTarget.AddLine(Text: string = '');
446begin
447 FTargetCode := FTargetCode + DupeString(' ', Indent) + Text + LineEnding;
448end;
449
450function TTarget.LongFileName(FileName: string): string;
451begin
452 Result := FileName;
453 {$IFDEF WINDOWS}
454 Result := '"' + FileName + '"';
455 {$ENDIF}
456 {$IFDEF UNIX}
457 Result := StringReplace(FileName, ' ', '\ ', [rfReplaceAll]);
458 {$ENDIF}
459end;
460
461constructor TTarget.Create;
462begin
463 inherited;
464 OptimizationLevel := coNormal;
465 BreakPoints := TBreakPoints.Create;
466 DebugSteps := TDebugSteps.Create;
467end;
468
469destructor TTarget.Destroy;
470begin
471 FreeAndNil(DebugSteps);
472 FreeAndNil(BreakPoints);
473 inherited;
474end;
475
476procedure TTarget.Reset;
477begin
478end;
479
480procedure TTarget.OptimizeSource;
481begin
482end;
483
484procedure TTarget.Compile;
485begin
486 LoadProgram;
487 if OptimizationLevel = coNormal then OptimizeSource;
488 Compiled := True;
489end;
490
491procedure TTarget.CompileToFile;
492var
493 Process: TProcess;
494 CompiledFile: string;
495 Lines: TStringList;
496 I: Integer;
497begin
498 CompiledFile := ExtractFilePath(ProjectFileName) +
499 CompiledDir + DirectorySeparator + Name + DirectorySeparator +
500 ExtractFileNameOnly(ProjectFileName) + SourceExtension;
501 ForceDirectories(ExtractFilePath(CompiledFile));
502 with TStringList.Create do
503 try
504 Text := FTargetCode;
505 SaveToFile(CompiledFile);
506 finally
507 Free;
508 end;
509 if CompilerPath <> '' then begin;
510 if FileExists(CompilerPath) then
511 try
512 Process := TProcess.Create(nil);
513 Process.CurrentDirectory := ExtractFilePath(CompiledFile);
514 Process.Executable := LongFileName(CompilerPath);
515 Process.Parameters.Add(LongFileName(CompiledFile));
516 for I := 0 to GetEnvironmentVariableCount - 1 do
517 Process.Environment.Add(GetEnvironmentString(I));
518 Process.Options := [poWaitOnExit, poUsePipes];
519 Process.Execute;
520 if Assigned(FOnLog) then begin
521 Lines := TStringList.Create;
522 Lines.LoadFromStream(Process.Output);
523 Lines.Insert(0, Process.Executable + ' ' + Process.Parameters.Text);
524 FOnLog(Lines);
525 Lines.Free;
526 end;
527 finally
528 Process.Free;
529 end else raise Exception.Create(Format(SCompilerNotFound, [CompilerPath]));
530 end;
531end;
532
533procedure TTarget.RunFromFile;
534var
535 Process: TProcess;
536 CompiledFile: string;
537 RunFile: string;
538begin
539 if ExecutorPath = '' then Exit;
540
541 CompiledFile := ExtractFilePath(ProjectFileName) +
542 CompiledDir + DirectorySeparator + Name + DirectorySeparator +
543 ExtractFileNameOnly(ProjectFileName) + CompiledExtension;
544 RunFile := ExtractFilePath(ProjectFileName) +
545 CompiledDir + DirectorySeparator + Name + DirectorySeparator +
546 ExtractFileNameOnly(ProjectFileName) + RunExtension;
547 if not FileExists(ExecutorPath) then
548 raise Exception.Create(Format(SExecutorNotFound, [ExecutorPath]));
549 if FileExists(CompiledFile) then
550 try
551 Process := TProcess.Create(nil);
552 if ExecutorPath <> '' then begin
553 Process.Executable := LongFileName(ExecutorPath);
554 Process.Parameters.Add(LongFileName(RunFile));
555 end else Process.Executable := LongFileName(RunFile);
556 Process.ShowWindow := swoShow;
557 Process.Options := [poWaitOnExit, poNewConsole];
558 Process.Execute;
559 finally
560 Process.Free;
561 end else raise Exception.Create(Format(SCompiledFileNotFound, [CompiledFile]));
562end;
563
564procedure TTarget.Run;
565begin
566end;
567
568procedure TTarget.Pause;
569begin
570end;
571
572procedure TTarget.Stop;
573begin
574end;
575
576procedure TTarget.StepOver;
577begin
578end;
579
580procedure TTarget.StepInto;
581begin
582end;
583
584procedure TTarget.StepOut;
585begin
586end;
587
588procedure TTarget.RunToCursor(Pos: Integer);
589begin
590end;
591
592procedure TTarget.LoadFromRegistry(Context: TRegistryContext);
593begin
594 with TRegistryEx.Create do
595 try
596 CurrentContext := Context;
597 CompilerPath := ReadStringWithDefault('CompilerPath', CompilerPath);
598 ExecutorPath := ReadStringWithDefault('ExecutorPath', ExecutorPath);
599 Selected := ReadBoolWithDefault('Selected', Selected);
600 finally
601 Free;
602 end;
603end;
604
605procedure TTarget.SaveToRegistry(Context: TRegistryContext);
606begin
607 with TRegistryEx.Create do
608 try
609 CurrentContext := Context;
610 WriteString('CompilerPath', CompilerPath);
611 WriteString('ExecutorPath', ExecutorPath);
612 WriteBool('Selected', Selected);
613 finally
614 Free;
615 end;
616end;
617
618procedure TTarget.LoadProgram;
619begin
620end;
621
622function TTarget.SourceReadNext: Char;
623begin
624 Result := ' ';
625end;
626
627end.
628
Note: See TracBrowser for help on using the repository browser.