WILSON@AMSTEL.llnl.gov (One Heppy Heppy 'Ket') (08/15/90)
Today's Ada Challenge: How can I force range checks on data presented to my Ada program from a foreign (viz., C) routine? Here's the problem. We have many Ada programs which receive many messages from many C programs. These C programs occasionally (erroneously) send messages with values not in the ranges defined by our interface, and hence not in the ranges defined by the associated Ada datatypes. By the time Ada gets involved, the data is already loaded (by the non-Ada network routines) into an Ada record-type variable. Our Ada compiler (VAX Ada V1.5) sees no reason to perform any range checks, since it didn't do the actual assignment which loaded the variable. Eventually the program performs an array index based on a component of the record variable, and gets an access violation, or worse, crashes a task, or who-knows-what. My feeling is that I would like to force a range check on all components of the record as soon as it is read in. Ideally, I would like to perform this check in the generic READ routine in my Ada interface to the network I/O system. However, even knowing the details of the record doesn't necessarily help. Here are some ideas I considered, and then discarded: 1. Assignment to another variable of the same type. Unfortunately, a smart optimizer (such as ours), will determine that no check is necessary, assuming that the source variable has already been checked. 2. Assignment to a variable of a derived type based on the record type. The same problem exists as with idea (1), except that the optimizer needs to be slightly smarter. 3. Try checking discrete fields individually, using "in". Same problem as (1). 4. Try checking discrete fields individually, using predefined operators. For example, for an integer subrange, perform the test: if MESSAGE.INT /= INT_TYPE'last then begin TEMP := INT_TYPE'succ( MESSAGE.INT ); exception when CONSTRAINT_ERROR => raise RANGE_CHECK_ERROR; end; end if; This last one (4) I think would work, but given the large number of programs, message types, and components in each message, (and the fact that it couldn't be implemented in the generic READ routine), this would be a major undertaking, and difficult to maintain and enforce over the project lifecycle. It would also add more overhead than necessary to the program, by performing the increment. It's also possible that a smart compiler might only raise CONSTRAINT_ERROR if the new value is exactly one greater than the upper limit, checking with "=" rather than ">", in which case even this test wouldn't work. So that's the story. I wish there were a pragma "CHECK" which could be placed anywhere in the proper body of a subprogram. In the absence of that, I'll welcome any suggestions from the net. Thanks in advance, --- Rick Wilson Lawrence Livermore National Laboratory (415) 423-6662 wilson@derby.llnl.gov
bhanafee@ADS.COM (Brian Hanafee) (08/17/90)
In article <4CE1FEE5846FC025DB@icdc.llnl.gov> WILSON@AMSTEL.llnl.gov (One Heppy Heppy 'Ket') writes: >Today's Ada Challenge: > > How can I force range checks on data presented to my Ada program from a >foreign (viz., C) routine? > [stuff deleted] > By the time Ada gets involved, the data is already loaded (by the non-Ada >network routines) into an Ada record-type variable. Our Ada compiler >(VAX Ada V1.5) sees no reason to perform any range checks, since it didn't do >the actual assignment which loaded the variable. Eventually the program >performs an array index based on a component of the record variable, and gets >an access violation, or worse, crashes a task, or who-knows-what. [list of possible approaches deleted] > > Thanks in advance, > > --- Rick Wilson > Lawrence Livermore National Laboratory > (415) 423-6662 > wilson@derby.llnl.gov How about defining a generic like this: generic type The_Range_Limited_Integer_Type is range <>; function Checked (Item : in Integer) return The_Range_Limited_Integer_Type; function Checked (Item : in Integer) return The_Range_Limited_Integer_Type is begin return The_Range_Limited_Integer_Type (Item); end Checked; and using it like this: function Checker is new Checked (The_Element_Type); The_Record.The_Element := Checker (The_Record.The_Element); Since the compiler doesn't know how the generic function is used, it can't assume the range will be good in all cases, so it should have to embed a range check in the function instantiation. The only time I can think of when the optimizer might catch this is if Checker gets inlined rather than called. You'll have to write generics for all the possible root types (e.g. float, long_integer, etc.). Enumerated types might be a little trickier. Brian Hanafee Advanced Decision Systems
prune@foxinsox.hf.intel.com.ogi.edu (08/17/90)
In article <4CE1FEE5846FC025DB@icdc.llnl.gov> WILSON@AMSTEL.llnl.gov (One Heppy Heppy 'Ket') writes: > > How can I force range checks on data presented to my Ada program from a > foreign (viz., C) routine? > > Here's the problem. We have many Ada programs which receive many messages > from many C programs. These C programs occasionally (erroneously) send > messages with values not in the ranges defined by our interface, and hence not > in the ranges defined by the associated Ada datatypes. > > By the time Ada gets involved, the data is already loaded (by the non-Ada > network routines) into an Ada record-type variable. Our Ada compiler > (VAX Ada V1.5) sees no reason to perform any range checks, since it didn't do > the actual assignment which loaded the variable. Eventually the program > performs an array index based on a component of the record variable, and gets > an access violation, or worse, crashes a task, or who-knows-what. The way I read this, your design problem is that you lack the direct language tools to define the *true* interface. C only recognizes certain types, and sees no fault with the values it hands to the Ada routines. The Ada compiler trusts that the programmer has taken care of any inter- language issues, so that you effectively have an unchecked conversion from C to Ada. Your Ada compiler has a fairly intelligent optimizer, so you can't fool it simply by sticking a finger up your nose and doing a little dance. I recommend "properly" defining the interface from the Ada side. Don't try to fool the optimizer: if Digital upgrades it in 2.0, you could find yourself SOL the next time you build your message system. For illustration, I will assume that your input contains an integer subtype, an enumeration type, and a text message. max_error_number: constant integer := 150; type snafu_number is byte_integer range 0 .. max_error_number; type snafu_type is (no_problem, drain_clog, gfci_fault, incoming_missile, dirty_diaper, <etc.> ); synopsis_limit: constant integer := 40; type snafu_string is string (1 .. synopsis_limit); type C_message is record -- This is what C sees error_number: byte_integer; error_class: byte_integer; error_text: string(1 .. 40); end; -- And now, the Ada side of the story: type Ada_message is record -- How Ada *wants* to see it numeric_code: snafu_number; error_type: snafu_type; synopsis: snafu_string; end; -- copy a C message into its Ada equivalent function convert_C_message_to_Ada_message (give: in C_message) return Ada_message is take: Ada_message; begin Ada_message.numeric_code := C_message.error_number; Ada_message.error_type := C_message.error_class; Ada_message.synopsis := C_message.error_text; exception -- handle range errors here to your heart's content end convert_C_message_to_Ada_message; This code can be added to your current system, in the Ada package that gets the information from the C routine. Simply feed a C_message to the C routine that is providing you with the input; then copy that to the Ada_message where you truly wanted the information. You've given the compiler all the information it can use about what the two records *really* contain, and it can optimize and/or in-line as it sees fit. I'm confident that the Digital compiler, like ours, will optimize out the redundant portions of the checks, and will make short work of what remains. -------------------------------------------------------------------------------- DISCLAIMER: Intel pays me well for the opinions it wants; the other stuff remains mine. Some of the latter is posted; it's worth what you paid for it. A buddhist nudist practices yoga bare. local backbone: tektronix myrddyn!prune@uunet.uu.net USA phone: (503) 696-4668 wickart@ijf2.hf.intel.com Copyright (C) 1990, William D. Wickart. All rights reserved.