Articles   Members Online:
-Article/Tip Search
-News Group Search over 21 Million news group articles.
-Delphi/Pascal
-CBuilder/C++
-C#Builder/C#
-JBuilder/Java
-Kylix
Member Area
-Home
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Login/Logout
-Become a Member
-Why sign up!
-Newsletter
-Chat Online!
-Indexes NEW!!
Employment
-Build your resume
-Find a job
-Post a job
-Resume Search
Contacts
-Contacts
-Feedbacks
-Link to us
-Privacy/Disclaimer
Embarcadero
Visit Embarcadero
Embarcadero Community
JEDI
Links
How to assertion Magic Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
10-Nov-02
Category
Algorithm
Language
Delphi 5.x
Views
54
User Rating
No Votes
# Votes
0
Replies
0
Publisher:
DSP, Administrator
Reference URL:
DKB
			Author: Ezra Hoch

The ASSERT function shows you the line number and unit name from which it was 
launched. How can you get this functionality for different useages in your code

Answer:

Any information in this article has been checked on Delphi5 and Delphi 6 only (on 
Pentium II processor). It might not work on any other configuration ! 

First, we need to understand how the ASSERT fucntion works. As you've probably 
noticed, when an assertion occures, the error raised includes the line number and 
unit name. How does this happen? Where does the ASSERT function get that info from? 

The answer is very simple. From Delphi's compiler. When Delphi compiles your code 
it addes a few lines in assembler code before the call to ASSERT. These assembler 
instructions contain the extra information the ASSERT function needs. 
That is, the line number from which it was called, and the unit's name. But what if 
you want to have that info? How can you retreive the line number and unit name of a 
specific statment in your code? The answer is : you can't (at least as far as I 
know), at least not strait forwardly, but you can get it in some tricky way. 

To solve this, we'll use Delphi's compiler. Since the compiler adds the extra 
information we're looking for before an ASSERT function, we'll put an ASSERT 
function in our code, and have the compiler add the needed information. Now we need 
to find a way to avoid the ASSERT function call (sinc we don't want a simple 
assertion error to happen). In order to do that, we'll add some assembler code to 
skip the ASSERT function. You might wonder why to put the ASSERT fucntion in the 
first place, if we're going to skip it in any case. The answer is, in order to read 
the extra information (line number and unit name) we have to let the compiler add 
it to our code, and that can be done only by adding the ASSERT function. 

After skipping the ASSERT function, we'll read the information added by the 
compiler (using assembler) and put it in some global variables. Afterwards you can 
do what ever you wish with that info. 

Here is the needed code : 

1   var
2     // Global variables to hold the result
3     GLineNumber: Integer;
4     GError, GUnitName: string;
5   
6    asm
7   
8   // Save EAX and EBX cause we're going to change them
9       push eax 
10      push ebx 
11  // The following 3 lines are in order to get the value of EIP
12  // "call" pushes EIP to the stack
13      call @Temp 
14      @Temp: 
15  // now we POP EIP into EBX
16      pop ebx 
17  // Add to EBX the value needed inorder to skip the ASSERT function
18      add ebx, $1A 
19  // Skip the ASSERT function by jumping to the code after it
20      jmp ebx 
21    end; 
22   ASSERT(False, 'Your Error Message Here');
23    asm 
24  // Make EBX point to the line number value that the compiler inserted
25      sub ebx, $13 
26  // Read that value into EAX
27      mov eax, [ebx] 
28  // Put the line number into GLineNumber
29      mov GLineNumber, eax 
30  
31  // Same as above but for GUnitName
32      add ebx, 5 
33      mov eax, [ebx] 
34      mov GUnitName, eax 
35  
36  // Same as above but for GError (the assertion error message)
37      add ebx, 5 
38      mov eax, [ebx] 
39      mov GError, eax 
40  
41  // Restore the values of EBX and EAX
42      pop ebx 
43      pop eax 
44  end;


Let's look back again on what this code does. 

Delphi's compiler adds some info before any ASSERT function it finds in the  code. 
We add a call to ASSERT to make the compiler add the wanted information 
We add assmbler code to skip the call to ASSERT 
We add code (after the call to ASSERT) that reads to information that the compiler 
added. 
We do what ever we wish with this information, for example - raise a better 
excpetion. 

Note : 

1) You might think this is to much code for such a simple task. Inorder to make 
this code shorter, you can but the assmbler code in a file, and use the {$I} 
include compiler directive. That way the code will look like this : 

45  {$I PreAssert.INC}
46  ASSERT(False, 'YourMessage');
47  {$I PostAssert.INC}


2) You might want to override the assertion handling method in System.Pas instead 
of all of this, but then you won't be able to use regular ASSERT functions when 
they are needed. 

3) This code is not meant to replace ASSERT. It just uses ASSERT inorder to 
retrieve the line number and unit name. This code is meant to get that information 
for a specific loacation in your code. 

4) You must pass "False" as the first parameter of the ASSERT function you write. If you pass "True" then the compiler completly ignores the function, and if you pass a condition (eg. i = 5) the compiler will generate more code then expected, and the assembler instructions that I've provided won't work properly.

			
Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10

 

Advertisement
Share this page
Advertisement
Download from Google

Copyright © Mendozi Enterprises LLC