Informix 4GLYour Informix 4GL application can have another twenty years — as readable Java you own, or running as itself on GraalVM. Prove it on your own source.Test it on your own 4GL
Está viendo la edición Perú. Está viendo la edición Colombia. You're viewing the Pakistan edition. Cambiar a la edición global →Cambiar a la edición global →Switch to the global edition →
4GL to Java · GraalVM

The same 4GL, running as itself on GraalVM.

The migration compiler turns a 4GL program into Java you own. A second backend over the same front end does something different : it runs the .4gl directly on GraalVM, as a language of its own, inside the process that already holds your connection pool and your transaction. Business rules written in 1994 execute beside JavaScript and Java in one heap, with the arithmetic Informix defined and no rewrite in between.

The same 4GL running as itself on GraalVM: a 4GL module executes beside JavaScript and Java in one process and one heap, next to the connection pool and the transaction, on one optimising compiler — with the arithmetic Informix defined and no rewrite in between.

One front end, two backends

Parsing, `LIKE` resolution against the live catalogue and module linking are the compiler's, unchanged. Only the last step differs : Java source in one path, executable nodes in the other. Nothing about 4GL is understood twice.

A real interpreter, not hidden code generation

No `.java` is produced and `javac` never runs. The node tree is walked, and once a routine is hot the partial evaluator inlines it and the compiler emits machine code for it.

One jar inside your server

A host that already evaluates other scripting languages adds 4GL by putting one jar on the classpath. It needs no knowledge of the compiler, the intermediate representation or Truffle.

Two implementations, one meaning

Both backends call the same runtime and the same SQL binder, and the same programs are run through both and compared — line for line for screen output, byte for byte for reports.

Why a second way to run a program is worth having at all.

A migration compiler answers one question : what does this program become. It is the right question, and the Java it produces is the deliverable — reviewed, owned and maintained by hand from cut-over onward. Nothing on this page changes that.

But there is a second question that a compiler cannot answer, and it arrives much earlier : what does this program do, right now, against real data, before anybody has agreed to keep its translation. Historically the only way to answer it was to run the original binaries on the original box — which requires the licence, the terminal and the operating system that the estate is trying to leave behind.

Running the 4GL as itself on a modern JVM answers that question without any of them. The same source, the same catalogue, the same arithmetic ; a process a developer can start on a laptop, embed in a server, and point at a copy of production. The migration is then a decision taken with evidence, rather than a leap taken on a schedule.

One front end, two backends

4GL source, .per forms and the live Informix catalogue enter a shared front end that parses, builds a sealed-record intermediate representation, resolves LIKE and links modules. From that one representation the migration backend emits Java which javac compiles into source the customer owns, while the GraalVM backend builds Truffle nodes that the partial evaluator turns into machine code once a routine is hot. Both call one runtime — Fgl, FglDecimal, SqlSession, FglReport, SqlHostVariables — and the two backends are held to the same output on the same programs.
The split happens after everything that defines what the program means. That is what makes two backends a design rather than a fork.

How an interpreter becomes machine code.

Truffle is the framework GraalVM provides for implementing a language as a tree of executable nodes. A language author writes the nodes ; the runtime supplies what turns them into fast code. When a routine has been executed enough times to be worth optimising, the partial evaluator specialises the node tree against everything it now knows to be stable — types that have not changed, branches never taken, values that are effectively constant — and the compiler emits machine code for the result.

The practical consequence is that the interpreter loop disappears from the hot path. A 4GL FOR loop that accumulates a total is not, after warm-up, a walk over node objects : it is compiled code with the loop body inlined into it, produced by the same compiler that optimises Java on the same JVM. Optimisation is tiered — a first, cheaper tier for code that is merely warm, a second for code that dominates — and the tiers are observable per routine, named with the 4GL function and the line it came from.

This is what makes the approach different in kind from an emulator or a shell-out. 4GL is not being simulated by a program written in another language. It is a language on the platform, compiled by the platform's compiler, sharing the platform's heap and garbage collector with everything else in the process.

What the platform gives a language, once it is written this way

None of it is 4GL-specific work. It follows from being a Truffle language on GraalVM rather than a generated program or an external process.

Source positions that survive optimisation

Every node knows the file, line and column it came from. That is why a statement the backend cannot yet run stops the program naming the exact line, rather than failing somewhere unrelated later, and it is the foundation the platform's tooling attaches to.

Peer languages in one process

A 4GL routine and a JavaScript function live in the same context and the same heap. Values pass between them without a serialisation format, a socket or a copy — there is no boundary to marshal across, because there is no boundary.

The host's connection, not its own

The engine opens no connection. It is lent the one the request already holds, inside the transaction the request already started, as the user the request already authenticated.

One JVM to operate

Heap, garbage collector, metrics, thread dumps, container limits and JVM flags apply to 4GL execution the way they apply to everything else the server runs. Operations learns nothing new.

4GL in the application server's execution console

A 4GL program open in the platform's query editor: MAIN with DEFINE statements for SMALLINT, CHAR and MONEY variables, WHENEVER ERROR handling and CREATE TEMP TABLE statements, with syntax highlighting, line numbers, a minimap and a Run Query control.
A 4GL program in the editor, ready to execute on GraalVM inside the application server.

The arithmetic is the part nobody can afford to lose.

Business rules are arithmetic before they are anything else, and 4GL's arithmetic is not the arithmetic most languages give you by default. DECIMAL and MONEY carry a declared precision and scale, and every operation on them is exact within it. A rewrite that reaches for a binary floating-point type — the default numeric in most languages a modernisation would target — does not reproduce those rules ; it approximates them, and the error appears as cents that will not reconcile, months after go-live, in a period close.

Both backends resolve this the same way, because both call the same FglDecimal : arbitrary-precision arithmetic carrying the declared precision and scale, with the rounding 4GL specified rather than the rounding the hardware happens to do. The value that comes out of a 4GL expression running on GraalVM is the value that came out of it on Informix — and the same value the migrated Java produces, because there is one implementation underneath all three.

The architecture page sets out what that costs and why the compiler refuses the naive mapping, with the worked arithmetic beside it.

4GL inside the application server

An HTTP request reaches the host application server, which owns the connection pool, transaction and security context. The host evaluates a script through one GraalVM polyglot context where JavaScript, Python, 4GL and host Java are peer languages in one process and one heap. The 4GL language is a single jar on the classpath. It opens no connection of its own, is lent the host's with close disarmed, writes DISPLAY output to the response writer, and resolves LIKE against the catalogue on that same connection.
The engine is a guest in someone else's transaction, and every rule in the panel exists to keep it behaving like one.

A guest in someone else's transaction.

Inside a server the connection is not the language's to open. The host already has one : pooled, transacted, audited, authenticated as the user whose request is being served. A language that opened its own would run outside that transaction, outside the pool, and as whatever user its own settings named — which is how a script becomes a hole in an access-control model that everything else in the estate respects.

So the connection is lent, through the standard scripting bindings, and three consequences follow deliberately. The engine opens none of its own. It never closes the one it is given — close() is disarmed on the borrowed connection, because closing a pooled connection mid-request would end the caller's transaction. And the catalogue that LIKE resolves against is read from that same connection, so a column's type is the type in the database the program will actually read, not one captured at build time from a database that has since moved on.

If no connection is lent and a script runs SQL, it stops at that statement and says how to lend one. It does not quietly find one elsewhere. For a language being introduced into an estate that already has a security model, refusing is the only safe default.

Compile it, or run it as itself — and when each is right

Run on GraalVM — inside a modern application serverCompile to Java — the migration deliverable
What you get✓ The .4gl running as itself — no Java generated, no build step in between. The source in the repository is the program in production.Java source, in your repository, packaged by Maven, maintained by hand from cut-over onward.
How it executes✓ As Truffle nodes under the Graal compiler : hot routines are partially evaluated into machine code, so 4GL runs with the same optimising JIT the platform's other languages enjoy — not as an interpreter loop.As javac-compiled bytecode on any JVM, JIT-compiled like the rest of your Java.
Where it runs✓ Inside a host application server on a GraalVM JDK, in the same process as the services around it.Any JVM — an ordinary Java application, artifact or container.
Enterprise integration✓ The server's request lifecycle applies to 4GL as to everything else it runs : the pooled connection is lent inside the caller's transaction, under the caller's authenticated identity, with the server's authorization roles and audit rail covering every call.An application you integrate yourself : REST endpoints, queues and schedulers are added as Java around the translated rules.
Beside other languages✓ A peer language beside JavaScript and Python under one supervised execution model — a 4GL rule is callable from any of them, in-process.Ordinary Java interop — the translated rules are methods any Java code can call.
Screens and dialogs✓ Not carried : terminal statements stop and name the line. The interface is rebuilt as a modern web front end over the same rules ; programs whose screens must stay as they are take the Java route.Full support — `MENU`, `INPUT`, `INPUT ARRAY`, `DISPLAY ARRAY`, `CONSTRUCT`, reports, forms.
Who owns it afterwards✓ The 4GL stays the source ; nothing is generated to review or maintain.Your developers, in a language they can hire for.
What it ends✓ The tie between the business rules and a character terminal — and the last reason to keep the original box alive.The 4GL runtime licence, the proprietary toolchain, the dependency on a language the market no longer teaches.
Use it for✓ The forward direction : a modern web application over rules that stay exactly as they are, with the full weight of an enterprise application server underneath them.The migration itself — when the application, screens included, must cross over as it is.

Testing a rule that runs on GraalVM, in the build you already have.

Both backends put 4GL under automated test, and it is worth being precise about how each does it, because they are not the same mechanism and the difference follows from what each one is for.

The Java backend drives screens : an in-memory terminal, keystrokes queued ahead of the run, assertions on what the dialog displayed. That is what you want for an order-entry program, and it is covered on the migration pages.

This backend has no screen, so it tests the thing it does have : the rules themselves, called directly. A 4GL program is reached through the standard Java scripting interface — the same one a server already uses for any other language it hosts — so a JUnit test evaluates it, captures its output through an ordinary writer rather than a terminal, and asserts on the result. Arguably it is the better unit test of a pricing rule, because it exercises the arithmetic without the screen that happened to be in front of it.

Errors behave the way the rest of your Java does : a failure inside the 4GL arrives as an ordinary scripting exception naming the 4GL line, so a red test points at a statement rather than at a stack you have to decode.

The strongest test available here is the two backends against each other.

The same 4GL source can be translated to Java and run as itself on GraalVM, which makes a test possible that no single implementation can offer : run the program both ways and compare the output line for line. Reports are compared byte for byte.

We hold our own backends to that on every build, and it is the shape we recommend to an estate mid-migration, because it answers the question that actually keeps people awake. Not "does the translation compile" — whether it still produces what the original produced. Two implementations of a language are two answers to what the language means, and the dangerous failure is never a crash ; it is both of them running and quietly disagreeing about a total.

The same technique compares either backend against the original Informix binaries, which is how a wave earns the right to go into production : the rules were not re-specified, and there is a test that says so on every build rather than a memory of a testing week.

What the GraalVM backend does not do, stated plainly.

It has no screen. There is no terminal to own when a program is reached from a debugger or from another language, so the statements that need one — OPEN FORM, MENU, INPUT, CONSTRUCT, DISPLAY ARRAY and their neighbours — stop the program and name the file and line. They are refused rather than skipped, and the difference matters : a MENU that quietly did nothing would not run the program without its menu, it would run a different program, leaving every variable the dialog should have set unset and breaking later somewhere with no visible connection to the cause.

Loading a program prints what it will need before anything runs, so the answer arrives at load time rather than halfway through a batch. Several constructs are also not lowered yet and are refused the same way : arrays, dynamic SQL, report GROUP blocks and their aggregates, GOTO, LOCATE, FREE and any FETCH that is not forward.

Which of your programs this reaches is a question about your programs, not a general claim, and it is answered the same way every time : load them and read what comes back. A program that needs nothing on that list runs ; a program that needs something on it says so, by name and line, before it starts. That takes minutes on a directory of 4GL, and it is the first thing an assessment does.

Where this leads : 4GL as one language among several.

Once 4GL is a language on a polyglot runtime rather than a program compiled ahead of time, the interesting question stops being how to leave it and becomes where to keep it. A pricing rule refined over twenty years is not technical debt because of the language it is written in ; it is an asset written in a language whose runtime became a liability. Separating those two things is what this backend is for.

The Airtool platform is built on the same runtime family, with server-side JavaScript, Python and Groovy already running as peer languages under one supervised execution model — see airtool.io/platform and, for the Informix-specific route, airtool.io/modernisation/from-informix. A 4GL routine placed there is not a foreign body being tolerated : it is a language in the same context, in the same heap, under the same security and audit model as everything around it, with the screen it used to own replaced by a modern interface and the rule inside it untouched.

That is the direction, and it is deliberately the second step. The first is still a migration whose deliverable is Java you own — see build and package. What changes is that the estate is no longer choosing between keeping 4GL forever and losing it entirely.

The distinction worth holding on to is that the two backends on this page change how your code runs, and the platform changes what it runs inside — which is why it is a forward path rather than a third backend. The forward path sets the three side by side.

One place this already runs, if you would rather not assemble it yourself.

This page describes a language that embeds in a GraalVM host you provide. Building that host — connection pool, identity, audit, multi-tenancy and the operations around them — is substantial work in its own right, and usually not the work anyone set out to do.

An enterprise application platform already exists that runs GraalVM polyglot scripting as its execution model, with this 4GL wired in beside JavaScript, Python and Groovy as a peer language : a 4GL rule runs on the caller's connection, inside the same permission perimeter and audit trail as everything else, callable from the other three. That is the embedding above, already assembled and operated. Whether it is interesting depends on how far you mean to go, and the route is on the forward path.

The paper.

The execution model is written up in full — what GraalVM is and why a language implemented on it inherits a mature runtime rather than building one, how partial evaluation turns an ordinary interpreter into a compiler, a real compilation trace of a 4GL routine read line by line, deoptimisation and why it is what makes aggressive optimisation safe, the embedding contract and the host-lent connection, and the limits stated plainly.

Informix 4GL on GraalVM (PDF) — written for the architect deciding where the business rules should live.

For the CIO and the engineering manager

What this commits you to.

Where this sits beside the migration compiler, what it rules out, and what it asks of the people who maintain the code.

Is this a replacement for the migration compiler?

No, and it is not offered as one. The deliverable is the Java the compiler produces : reviewed, owned and maintained by hand. The GraalVM backend adds a way to run, embed and prove a program before anybody decides to keep its translation, and a place for rules that are worth keeping in 4GL. The dependency runs one way — the GraalVM backend is built on the compiler's front end, and nothing in a migrated application ships it.

If we run programs this way now, does that close off the Java route later?

No, and it is arranged so that it cannot. Both backends sit on one front end : the parsing, the LIKE resolution against the live catalogue and the module linking happen once, and only the last step differs. Running a program is therefore not a commitment about it — the same unchanged .4gl can be translated afterwards, and translated module by module while the rest keeps running. The dependency runs one way, so nothing a migrated application ships carries this backend with it. In practice running comes first for a different reason than convenience : it is the cheapest way to prove a program behaves as it always did before anybody decides whether its translation is worth keeping.

Can we run our interactive screen programs this way?

Not on this backend. Statements needing a terminal are refused with the file and line, and the whole list is printed when the program loads. Screen programs run through the Java backend, which has the full dialog and forms implementation. In practice the split falls out naturally : batch, reports, calculation and rules embed ; operator screens migrate.

How do you know the two backends agree?

Because they are made to share everything that could disagree, and then tested on the difference. Both call the same runtime helpers from the same table, and one SQL binder places the placeholders for both. The same programs then run through both paths and the output is compared line for line ; report output is compared byte for byte, against real Informix 4GL as the reference. Two implementations of a language are two answers to what it means, and the dangerous failure is not a crash — it is both running and quietly disagreeing.

What does this ask of the developers who maintain the 4GL today?

Very little, which is the point. The .4gl file stays the artifact — it is not converted, wrapped or regenerated, so what a developer edits, reviews and commits is what they edited, reviewed and committed before. The knowledge that took years to accumulate stays useful rather than becoming a translation problem. Where the working day changes is around the program : it now runs inside a host that owns the connection, the transaction and the security context, so the operational questions move from the program to the host. Nobody has to learn a new language in order to keep the rules working.

For the architect

How it executes.

What the runtime actually does with a program, what it needs underneath it, and what the host has to know.

Does running on GraalVM mean the 4GL is interpreted, and therefore slow?

No. It starts interpreted and does not stay that way : once a routine is executed enough to be worth optimising, the partial evaluator specialises the node tree and the compiler emits machine code for it — the same compiler that optimises Java in the same process. And for a business program the question turns out to matter less than it sounds, because the time is in the database rather than in the language. The SQL is unchanged and runs against the same server ; the loops and arithmetic around it end up compiled. A program that was fast enough on the original runtime is comfortably fast enough here.

Does it need a GraalVM JDK?

To get compiled code, yes — a current GraalVM release, or the runtime artifact the build declares. GraalVM Community Edition is open source and freely downloadable from graalvm.org/downloads, and it is a JDK : your Java applications run on it unchanged. On a JVM without it, the language still runs as a plain tree interpreter — correct, and slower. For embedding, the language jar shares the host's runtime rather than carrying its own, which is why the host's JVM choice is the one that matters.

What does the host application server have to know about Truffle?

Nothing. It puts one jar on the classpath and evaluates a script through the standard scripting interface, exactly as it would for any other language it hosts. ANTLR inside the jar is relocated so a host with its own copy is unaffected, and both the polyglot runtime and the Informix JDBC driver are deliberately left outside it — the host owns those, and a second copy of either in one process is a support call nobody wants.

For the developer and the DBA

Working with it day to day.

Crossing the language boundary, and whose connection the SQL runs on.

Can a 4GL program and a JavaScript function call each other?

They are peer languages in one polyglot context, sharing one heap, so values move between them without serialisation or a network hop. What a given host exposes across that boundary is the host's decision, not the language's.

Which connection does a program's SQL actually run on?

The one the host lends it. The engine opens no connection of its own and closes nothing it was given — a connection closed mid-request would end the caller's transaction — so the program's statements run inside the caller's transaction, on the caller's pool, under the caller's security context. The catalogue is read from that same connection, which is where a record declared LIKE a table gets its members, their order and each member's width and scale. For the DBA this means the session on the server is the host's session : the SQL is unchanged, it runs against the same server, and there is no second pool to size or monitor.

Bring us a program and we will run it both ways.

The most useful first conversation is a real one : one of your 4GL modules, translated to Java and run on GraalVM against a copy of your data, with the arithmetic compared against what the original produces today.