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.
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.
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.
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()
.
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.
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:
retval
field containing the value being suspended
Resume()
method for generating subsequent values
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:
this.retval
and returning itself
("return this;
")
iRuntime.Error()
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,
which encompasses the entire definition of the anonymous subclass of vClosure.return new vClosure() {
...}.Resume();
It is very important that the retval
field be set
before returning a vClosure object; a null retval
is illegal.