Interfacing EWB with Tricky JavaScript

by Erick Engelke
September 29, 204

I had an epiphany when I was thinking about how to solve my Nice Re-compiler challenges. That lead to this simplifed method described in this article.

I know of four ways to interface EWB with JavaScript.

Methods to Interface With JavaScript

Method Upside Downside

TScript / external fns/variables in JavaScript files

Easiest to understand, well documented by Elevate

Run into limitations including having to wait for libraries to load.

call JavaScript through CreateObject()

inline, doesn’t need to load external libraries

often awkward, also local vars not accessible, compressed EWB files add challenges with renaming of variables and functions

Nice recompiler

can nicely mix EWB + JavaScript

There is a bug in EWB’s compiler with Variants that makes it awkward. Also, Compressed functions can make more complicated.

Dynamic Functions (the topic of this blog article)

solves most of above problems

interface code must be written in pure JavaScript.

What do I mean by Dynamic Functions?

JavaScript allows you to replace a function in real-time while a program is running.

I will give the example of a JavaScript debugger where you can set a breakpoint in your code. Note, for this to work you must be using an HTML5 compatible browser (Edge, Chrome, Firefox, Safari, etc.) and have the JavaScript debug window open for the breakpoint to show.

You take the following steps.

Steps to Create Dynamic Functions

Name Example

Define Type of your new function or procedure

Type
TDebugger = procedure( title : string );

Declare variable to hold that new function or procedure.

var
QuickDebugger : TDebugger;

Implement code to "compile" your new JavaScript function. It can process local-to-it varibles and can also access global EXTERNAL variables (since externals will not be renamted during compressed compiles).

procedure InstallQuickDebugger;
begin
if not assigned( quickdebugger ) then
quickdebugger := TQuickDebugger( variant(CreateObject('function(title) { window.alert("Debug: " + title ); debugger; }')) );
end; `

Call the installation function first

InstallQuickDebugger();

Call the new function/procedure when needed

quickdebugger('some title');

Example In Code

Here’s the same code laid out in a program:

type
  TQuickDebugger = procedure( title : string );

var
  quickdebugger : TQuickDebugger;

procedure InstallQuickDebugger;
begin
  if not assigned( quickdebugger ) then
     quickdebugger := TQuickDebugger( variant(CreateObject('function(title) { window.alert("Debug: " + title ); debugger; }')) );
end;


procedure TForm1.Form1Show(Sender: TObject);
var
  v : variant;
  tproc3 : TTestProc3 ;
  tfn3 : TTestfn3;
begin

  InstallQuickDebugger;
  quickdebugger( 'startup code' );
  ...

When using this function, the browser will pop up the message: Debug: startup code

sample

and if you have JavaScript debugger enabled, will show something like

sample Javascript

Press single step twice, and you will sit nicely after the code point your placed your breakpoint.

:sample JavaScript

Creating Stubs

Pascal has many benefits for most of your program, such as its strong type checking that finds errors before run-time, and the namespaces which allow cleaner separation of variable names.

Where this method shines is calling library or browser functions that cannot be done easily in EWB.

I recommend doing as little in pure JavaScript as possible. Intead, create short stubs that just pop into JavaScript for the bare minumum, and do all the real workload in Object Pascal.

Using Overloaded Functions

If you know the variable types of the parameters you are passing, by all means, delcare your function declaration with those types.

But many Javascript functions take naked variables (could be Pascal string, integer, double, function, etc.).

If it will take multiple types, you have two choices. You can:

Table 1. Methods to Interface With JavaScript
Solution Advantage Disadvantage

declare overloaded functions for each supported type

EWB will enforce the types correctly

use variant

Faster to write

will allow you to call with unsupported types

See the notes below on variant issues…​

Obscure

Note: at present there are some very obscure bugs (in my opinion) in EWB’s Object Pascal compiler relating to variants;

If you declare: var external fn : TProcedure; variant v;

v := fn;

the generated code is: v = fn();

whereas it should be v = fn;

Also, you cannot assign functions or procedures without first declaring and using a pointer to that function, whereas you should be able to do so.

Post Analysis and Actual Solution…​.

After doing all that work, I had a second epiphany, all this work was started when I wanted to create a function which called JavaScript:

debugger;

After it was all done and published, I suddenly realized had been thinking too hard. I could accomplish this with a variable declaration and a simple statement;

var
  external debugger ; integer;

procedure test;
begin
  debugger ;

  // code to debug
  ....

And it works.

Erick