Dynamic Loading in Jcon

The capabilities of Icon can be extended through the use of dynamic loading at execution time. The built-in function loadfunc(libname,procname) loads the compiled Icon or Java procedure procname from the Zip archive libname and returns a procedure value. This value can be called just like any other procedure.

If libname is null, the procedure must be a Java procedure, and it is loaded from the same file as the current executable. (Icon procedures linked into the executable are not dynamically loadable.)

While the library archive can be built ahead of time, it is also possible for the running program to generate code and then build it by calling system() with the appropriate commands.

In Version 9 of Icon, loadfunc() loads procedures written in C. Jcon, in contrast, loads precompiled Java or Icon procedures. Although the Icon interface is similar, it is not possible to load the same procedure with both systems.

Preparing Icon Procedures

An Icon procedure is prepared for dynamic loading by compiling it with a command such as jcont -c file.icn. This produces a file.zip archive suitable for use with loadfunc().

When a Zip file of Icon procedures is first referenced by loadfunc(), all the globals and procedures in the file are linked before the requested procedure is returned. Subsequent loadfunc() calls can access other procedures from the file, but the file is not relinked when this is done.

A dynamically loaded Icon procedure can reference globals and procedures defined in the original program, its own source file, and any other files loaded before its own file is first linked. Unreferenced procedures must be declared invocable if they are to be referenced by subsequently loaded procedures.

Preparing Java Procedures

Java code can be used to provide programs with additional capabilities not expressible in Icon. Compilation of Java code produces .class files which are then bundled up by the jar utility to produce libraries for dynamic loading.

Construction of Java procedures requires not only a knowledge of Java but also some understanding of Jcon's run-time system. A tutorial on that subject is far beyond the scope of this discussion. It is hoped that the key points presented here, combined with inspection of the examples and the Jcon source code, will provide enough of a foothold to allow at least the construction of simple procedures. An understanding of Java is assumed in what follows.

Run-time system basics

The Jcon run-time system is contained in the source directory named jcon and forms a Java package of that name. A file containing a loadable procedure declares import jcon.*; to gain access to the jcon namespace.

Icon values of the various types are instances of classes such as vInteger, vString, and vList. Most of these classes implement a factory method such as vReal.New(3.14159) for constructing new instances. All are subclasses of a parent class vValue. Note that Icon strings are not implemented by the Java String class but instead by code in the vString class.

The class vDescriptor is the superclass of most run-time classes. It encompasses vValue as well as other objects that represent things such as subscripted strings. The vDescriptor.java source file lists a large number of methods that operate on vDescriptors and vValues. A vDescriptor d can be dereferenced to produce a vValue either by calling d.deref() or by calling an operation that implicitly dereferences it, such as d.Negate().

Procedures in Java

In Java, an Icon procedure is a subclass of vProc that defines a public Call() method that returns a vDescriptor object.

A procedure that expects two arguments extends the class vProc2 (which extends vProc) and defines a Call method that accepts two vDescriptor arguments and returns a vDescriptor result. More generally, a procedure expecting n arguments, for 0 <= n <= 9, extends vProcn and declares n vDescriptor arguments.

A procedure that expects more than nine arguments is written by extending the class vProcN and declaring a Call method that accepts an array of vDescriptors as its single argument. vProcN can also be used for any other procedure when an array of arguments is more convenient than using a fixed argument list.

The arguments passed to the Call method are not dereferenced. In the Jcon implementation, this is the responsibility of the called procedure. Often this is done by using the vDescriptor arguments in operations that implicitly dereference them.

The Call method returns a Java null to fail or a vDescriptor, usually a vValue, to succeed. (An Icon null value is produced by calling vNull.New() and returning the result.) Suspension will be covered in the next subsection.

Here is a procedure that accepts three arguments, coerces them to integer, and returns the sum:

import jcon.*;

public class sum3 extends vProc3 {

    public vDescriptor Call(vDescriptor a, vDescriptor b, vDescriptor c) {
        vInteger i = a.mkInteger();
        vInteger j = b.mkInteger();
        vInteger k = c.mkInteger();
        return vInteger.New(i.value + j.value + k.value);
    }

}

This procedure could be used as follows:

procedure main()
   local sum3
   sum3 := loadfunc("sum3.zip", "sum3")
   write(sum3(5, 8, 11))
end

With the source code in sum3.java and sumtest.icn, the shell commands would be something like this:

jcont -u sumtest.icn
setenv CLASSPATH /myhome/jcon/bin/jcon.zip
javac sum3.java
jar cf sum3.zip sum3.class
./sumtest

Many examples of procedures can be found in the jcon/f*.java files in the Jcon distribution. These files implement Icon's built-in functions.

Suspension

A procedure suspends by returning an instance of class vClosure. This is another subclass of vDescriptor, so the declaration of the Call method does not change. The vClosure object encapsulates two key items:

In general, any procedure that suspends requires its own subclass of vClosure to implement its particular Resume() method. Java's "inner classes" are useful for this.

The Resume() method takes no arguments and returns a vDescriptor. It is called to produce the next value when the suspended procedure is resumed. Resume() can do one of four things:

It is not possible for Resume() to "return" in the Icon sense. It must instead suspend a value and then fail upon later resumption.

Here is an example of a procedure that generates the factors of an integer. To avoid a special case, even the first value is produced by calling the Resume() method.

import jcon.*;

public class factors extends vProc1 {

    public vDescriptor Call(vDescriptor a) {
        final long arg = a.mkInteger().value;
        return new vClosure() {
            long n = 0;
            public vDescriptor Resume() {
                while (++n <= arg) {
                    if (arg % n == 0) {
                         retval = vInteger.New(n);
                         return this;
                    }
                }
                return null; /*FAIL*/
            }
        }.Resume();
    }

}

The vClosure object is created, called, and returned by the large return expression,

return new vClosure() { ... }.Resume();
which encompasses the entire definition of the anonymous subclass of vClosure.

It is very important that the retval field be set before returning a vClosure object; a null retval is illegal.


index || intro | usage | differences | graphics | dynamic loading | performance | release notes | installation | references || home