Errors and limitations of using FastScript

The My Visual Database application development environment uses FastScript, a scripting library created by FastReports LLC, to implement additional functionality. In addition to undeniable advantages, this library has a number of limitations and defects. Some of them are described in the documentation (“Developer’s Guide”), but some of them are not documented and are unlikely to be fixed (the current documentation is dated 2005).

Official restrictions

The following are the FastScript limitations that matter when building applications in My Visual Database:

  • there are no declarations of types and classes in the script;
  • no records (records);
  • no pointers;
  • no sets (sets);
  • no shortstrings type;
  • no unconditional jump (GOTO)

Despite the absence of sets, it is possible to work with them, that is, it is possible to use the ‘IN’ operator – “a in [‘a’..’c’,’d’]”, and the absence of the GOTO operator should only please the true adherents of the structural approach in programming.

Undocumented bugs

Local variables, parameters and recursion

FastScript supports the passing of value-parameters and variable-parameters, however, when a procedure or function is called recursively, the value of the variable-parameter is not preserved.

Example. The Recursive() function calls itself as many times as specified in the ACount parameter, and the number of calls is accumulated in the AResult variable.

function Recursive( ACount: integer; var AResult: integer ): boolean;
begin
  AResult := AResult + 1;
  if count > 0 then
    Result := Recursive( count - 1, AResult )
  else
    Result := True;
end;Code language: Delphi (delphi)

However, in reality, the number 1 is always returned in the AResult variable:

procedure Form1_Button1_OnClick (Sender: TObject; var Cancel: boolean);
var
  r: integer;
begin
  r := 0;
  Recursive(10,r);
  ShowMessage(r);
end;
Code language: Delphi (delphi)

After careful review, it has been found that recursive calls are not actually supported, since all local variables, including variables for storing parameters, are created statically, without using the procedure call stack. That is, their values cannot be used when exiting a recursive call!

Autocorrect character combinations

If you type in the editor the sequence " after compiling the script.pas file to script.dcu, it will automatically turn into a “double quote” character.

Solution: the problem is solved by splitting the string sequence into pieces:

s := '&q'+'uot;';Code language: Delphi (delphi)

You cannot declare a parameter with a default value of nil

Kind parameter declarations are not allowed

procedure MyProc( AObject:TObject = nil)Code language: Delphi (delphi)

As a consequence, if an object is passed as a parameter, then that parameter cannot be optional.

You cannot use a constant when describing a parameter’s default value

Kind parameter declarations are not allowed:

const<br>  DEF_VAL = 1<br>procedure MyProc( AParam: integer = DEF_VAL )Code language: Delphi (delphi)

You must explicitly specify the value:

procedure MyProc( AParam: integer = 1 )Code language: Delphi (delphi)

No type casting

This is more of a limitation than a bug. But the fact remains: there is no explicit conversion of values to the desired type in FastScript.

In practice, this is not often encountered, since functions are used to convert values of different types or the conversion is done implicitly, but in Delphi you can convert object references to an Integer value (and vice versa), which, for example, is convenient for storing integer values in the array TStrings.Objects() which can be used to create a named array. However, this task can be solved differently by storing data in the TStrings.Strings() property in the form =, using the TStrings.Names() and TStrings.Values() properties to access the data. This will work a little slower, since it will require converting the string to a number.

Error -1:-1

The most incomprehensible, mysterious and intractable glitch. From time to time, the compiler cannot determine which line it encountered an error on. This usually manifests itself if there are several modules in the project, but I could not establish the exact cause of its occurrence.

Finding a line with an error in this case is very difficult: you need to visually look through hundreds or even thousands of lines of source code. Personally, to localize this error, I had to consistently comment on the bodies of procedures, since the “blurred” eye was simply unable to see the error in the code.

Objects, Recursion, and Event Handling

In one of the projects, recursive editing of tables was implemented: an editing form can be called from a table view form, and a table view form can be called from an edit form, and so on. Since the editing actions were identical, the same handlers were installed on different forms and different components. As it turned out, this led to an error, since when the handler is called, an object is used as a parameter, but objects, apparently, are processed as variable parameters (see the description of the error “Parameters variables are not saved during a recursive call” above), that is, they are not entered to the stack. As a result, errors that are inexplicable at first glance occur, which tracing helped to identify – a step-by-step recording of the states of variables and the names of the objects used.

This is a kind of latent bug because you can call handlers recursively, but you can’t use parameter values like Sender:TObject.

The same error occurs if there is a local variable in the event handler that holds a reference to an object. When called recursively, the value of the variable is overwritten!

A feature of the FastScript implementation imposes restrictions on the recursive call of event handlers that use variables or parameters that refer to objects.

Solution: if it is impossible to avoid using objects as parameters (for example, in an event handler), and recursion is required in the procedure, then another procedure must be called inside the recursive procedure, with text parameters (not objects).

CASE without operators

If you write a case statement like this, expecting that the commands in the else section will be executed for all values of A, except those explicitly specified (1,2 and 3), then you will be in for a surprise: the else section will always be executed!

case A of
1: ;
2: ;
3: ;
else
begin
  ShowMessage(A);
end;Code language: Delphi (delphi)

Solution: If you add any command after the selector value, then CASE will work as expected.

case A of
1: B := 1;
2: C:= B * 4;
3: D := A;
else
begin
  ShowMessage(A);
end;Code language: Delphi (delphi)

Leave a Reply

Your email address will not be published. Required fields are marked *