VMS Tech Demo 17 - JVM language calling native language

Content:

  1. Introduction
  2. Examples
  3. How it works
  4. Note about performance
  5. Fortran
  6. Pascal
  7. Basic
  8. C

Introduction:

Several of my previous articles has used my VMSCALL library to enable a JVM language (Java, Kotlin, Scala, Groovy, Jython or some lesser known) to call VMS native code (Fortran, Pascal, Basic, C etc.).

This article will do a deeper dive.

Expectations of the reader are:

Examples:

We will see 7 examples:

  1. integer in arguments
  2. string in arguments
  3. integer in and out arguments
  4. string in and out arguments
  5. integer inout argument
  6. string inout argument
  7. record inout argument

There are obviously more possible combinations, but those 7 should provide enough to let the reader understand how to handle other cases for arguments.

The examples will use the natural data types for the languages, which mean that the examples will have small variations - Fortran and Basic will be getting fixed length strings by class S descriptor, Pascal will be getting a varying of char by reference and C will be getting a null terminated char array.

The requirements for this to work are:

  1. native code is linked to a shared executable (required by JNI)
  2. all functions return an integer intended but not enforced to be a status code (required by the VMSCALL library - done that way because all the LIB$ and SYS$ functions work that way)

VMSCALL library is available here.

How it works:

Architecturally it looks like:

VMSCALL library stack

The syntax is:

call("name of shareable exe", "name of function", pass(argwrapper_1).byXxxx().yyyYyy(),
                                                  pass(argwrapper_2).byXxxx().yyyYyy(),
                                                  ...
                                                  pass(argwrapper_n).byXxxx().yyyYyy())

where the arg wrappers have the types:

Native type VMSCALL wrapper
16 bit integer Word
32 bit integer LongWord
64 bit integer QuadWord
fixed length string CharacterString
variable length string
(Pascal varying of char)
VariableCharacterString
null terminated string
(C null terminated char array)
NullTermCharacterString
any record/struct Block

and .byXxxx() is one of:

and .yyyYyy() is one of:

These should look very familiar if one knows VMS calling convention and traditional VMS documentation.

A few examples:

stat = call("myshr", "myfunc1", pass(new Word(1)).byReference().readOnly(), new LongWord(2).byReference().readOnly())
stat = call("myshr", "myfunc2", pass(new CharacterString("XXXXXXXXXX")).byDescriptor().readOnly())
wrapper = new LongWord()
stat = call("myshr", "myfunc3", pass(wrapper).byReference().writeOnly())
lw = wrapper.getValue()
wrapper = new CharacterString(256)
stat = call("myshr", "myfunc4", pass(wrapper).byReference().writeOnly()
cs = wrapper.toString()

Records/structs are done as classes with annotations.

General syntax:

…
@Struct(…)
class ClassName {
                @Field(n=0,type=FieldType.xxxx,…)
                typename fieldname
                @Field(n=1,type=FieldType.xxxx,…)
                typename fieldname
                …
}

Where the type xxxx is one of: INT1, INT2, INT4, INT8, UINT1, UINT2, UINT4, FP4, FP8, VMSTIME, UNIXTIME, FIXSTR, FIXSTRNULTERM, VARSTR, VARFIXSTR, PACKEDBCD, VAXFP4, VAXFP8.

Examples:

@Struct
class Simple {
                @Field(n=0,type=FieldType.INT4)
                int iv
                @Field(n=1,type=FieldType.FIXSTR,length=8)
                String sv
}
@Struct(alignment=Alignment.ALIGN4)
class SomeIntegers {
                @Field(n=0,type=FieldType.INT1)
                int iv1
                @Field(n=1,type=FieldType.INT2)
                int iv2
                @Field(n=2,type=FieldType.INT4)
                int iv3
}
@Struct
class SomeStrings {
                @Field(n=0,type=FieldType.FIXSTR,length=8) // Fortran CHARACTER*8
                String sv1
                @Field(n=1,type=FieldType.FIXSTRNULTERM,length=8) // C char[8]
                String s2
                @Field(n=2,type=FieldType.VARFIXSTR,length=8) // Pascal varying [8] of char
                String sv3
}

All the string filed types also support encoding=”utf-8” if the default of iso-8859-1 is not desired.

Note about performance:

A VMSCALL call is relative expensive mostly because of the expensive handling of parameters.

Simple data types are done like:

Records are even more complex:

Is that a problem? Probably not if one avoid doing something stupid!

Example: If you need to make a billion calls of some native function, then if you do a single VMSCALL call and have some native code do the 1 billion iterations calling the native function you will be fine, but if you let your JVM code do the 1 billion iterations each making a VMSCALL call to the native function, then it may be slow.

Fortran:

Native side:

n.for:

c--
c in integer arguments
c--
      integer*4 function nadd(a,b)
      integer*4 a,b
      integer*4 c
      c = a + b
      write(*,100) a,b,c
      nadd = 1
100   format(1x,'Fortran says : ',i,' + ',i,' = ', i)
      end
c--
c in character arguments
c--
      integer*4 function nconc(sa,sb)
      character*(*) sa,sb
      character*1024 sc
      sc = sa // sb
      write(*,100) sa,sb,trim(sc)
      nconc = 1
100   format(1x,'Fortran says : ',a,' + ',a,' = ', a)
      end
c--
c in and out integer arguments
c--
      integer*4 function naddret(a,b,c)
      integer*4 a,b,c
      c = a + b
      naddret = 1
      end
c--
c in and out character arguments
c--
      integer*4 function nconcret(sa,sb,sc)
      character*(*) sa,sb,sc
      sc = sa//sb
      nconcret = 1
      end
c--
c inout integer argument
c--
      integer*4 function nadd1(v)
      integer*4 v
      v = v + 1
      nadd1 = 1
      end
c--
c inout character argument
c--
      integer*4 function nconcx(s)
      character*4 s
      s = trim(s) // 'X'
      nconcx = 1
      end
c--
c inout record argument
c--
      integer*4 function nrecmod(r)
      structure /myrec/
        integer*1 b1
        integer*1 b2
        integer*2 w
        integer*4 lw
        integer*8 t
        character*8 s
      end structure
      record /myrec/r
      r.b1 = r.b1 + 1
      r.b2 = r.b2 + 1
      r.w = r.w + 1
      r.lw = r.lw + 1
      call sys$gettim(%ref(r.t))
      r.s = trim(r.s) // 'X'
      nrecmod = 1
      end

Build:

$ for n
$ link/share=nfshr n + sys$input/opt
SYMBOL_VECTOR=(NADD=PROCEDURE,-
               NCONC=PROCEDURE,-
               NADDRET=PROCEDURE,-
               NCONCRET=PROCEDURE,-
               NADD1=PROCEDURE,-
               NCONCX=PROCEDURE,-
               NRECMOD=PROCEDURE)
$
$ define/nolog nfshr "''f$parse("nfshr.exe")'"
$ exit

JVM side:

JF.java:

import java.io.UnsupportedEncodingException;
import java.text.DateFormat;;
import java.text.SimpleDateFormat;;
import java.util.Date;

import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.RecordException;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;

import static dk.vajhoej.vms.call.VMS.*;

public class JF {
    //--
    // in integer arguments
    //--
    private static void test1() {
        int a = 123;
        int b = 456;
        int stat = call("nfshr", "NADD", pass(new LongWord(a)).byReference().readOnly(),
                                         pass(new LongWord(b)).byReference().readOnly());
    }
    //--
    // in character arguments
    //--
    private static void test2() throws UnsupportedEncodingException {
        String sa = "ABC";
        String sb = "DEF";
        int stat = call("nfshr", "NCONC", pass(new CharacterString(sa)).byDescriptor().readOnly(),
                                          pass(new CharacterString(sb)).byDescriptor().readOnly());
    }
    //--
    // in and out integer arguments
    //--
    private static void test3() {
        int a = 123;
        int b = 456;
        LongWord cwrap = new LongWord();
        int stat = call("nfshr", "NADDRET", pass(new LongWord(a)).byReference().readOnly(),
                                            pass(new LongWord(b)).byReference().readOnly(),
                                            pass(cwrap).byReference().writeOnly());
        int c = cwrap.getValue();
        System.out.printf("Fortran gave : %d + %d = %d\n", a, b, c);
    }
    //--
    // in and out character arguments
    //--
    private static void test4() throws UnsupportedEncodingException {
        String sa = "ABC";
        String sb = "DEF";
        CharacterString cswrap = new CharacterString(1024);
        int stat = call("nfshr", "NCONCRET", pass(new CharacterString(sa)).byDescriptor().readOnly(),
                                             pass(new CharacterString(sb)).byDescriptor().readOnly(),
                                             pass(cswrap).byDescriptor().writeOnly());
        String sc = cswrap.toString();
        System.out.printf("Fortran gave : %s + %s = %s\n", sa, sb, sc);
    }
    //--
    // inout integer argument
    //--
    private static void test5() {
        int v = 123;
        LongWord vwrap = new LongWord(v);
        int stat = call("nfshr", "NADD1", pass(vwrap).byReference().readWrite());
        v = vwrap.getValue();
        System.out.printf("Fortran gave : %d\n", v);
    }
    //--
    // inout character argument
    //--
    private static void test6() throws UnsupportedEncodingException {
        String s = "ABC";
        CharacterString swrap = new CharacterString(s, 1024);
        int stat = call("nfshr", "NCONCX", pass(swrap).byDescriptor().readWrite());
        s = swrap.toString();
        System.out.printf("Fortran gave : %s\n", s);
    }
    //--
    // inout record argument
    //--
    private static DateFormat df = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
    @Struct
    public static class MyRec {
        @StructField(n=0, type=FieldType.INT1)
        private int b1;
        @StructField(n=1, type=FieldType.INT1)
        private int b2;
        @StructField(n=2, type=FieldType.INT2)
        private int w;
        @StructField(n=3, type=FieldType.INT4)
        private int lw;
        @StructField(n=4, type=FieldType.VMSTIME)
        private Date t;
        @StructField(n=5, type=FieldType.FIXSTR, length=8, pad=true, padchar=' ')
        private String s;
        public MyRec() {
            this(0, 0, 0, 0, null, null);
        }
        public MyRec(int b1, int b2, int w, int lw, Date t, String s) {
            this.b1 = b1;
            this.b2 = b2;
            this.w = w;
            this.lw = lw;
            this.t = t;
            this.s = s;
        }
        public int getB1() {
            return b1;
        }
        public void setB1(int b1) {
            this.b1 = b1;
        }
        public int getB2() {
            return b2;
        }
        public void setB2(int b2) {
            this.b2 = b2;
        }
        public int getW() {
            return w;
        }
        public void setW(int w) {
            this.w = w;
        }
        public int getLw() {
            return lw;
        }
        public void setLw(int lw) {
            this.lw = lw;
        }
        public Date getT() {
            return t;
        }
        public void setT(Date t) {
            this.t = t;
        }
        public String getS() {
            return s;
        }
        public void setS(String s) {
            this.s = s;
        }
        @Override
        public String toString() {
            return String.format("(%d,%d,%d,%d,%s,%s)", b1, b2, w, lw, df.format(t), s);
        }
    }
    @SuppressWarnings("unchecked")
    private static void test7() throws RecordException {
        MyRec r = new MyRec(1, 2, 3, 123, null, "ABC");
        Block rwrap = new Block(r);
        int stat = call("nfshr", "NRECMOD", pass(rwrap).byReference().readWrite());
        r = (MyRec)rwrap.getObject(MyRec.class);
        System.out.printf("Fortran gave : %s\n", r);
    }
    public static void main(String[] args) throws UnsupportedEncodingException, RecordException {
        test1();
        test2();
        test3();
        test4();
        test5();
        test6();
        test7();
    }
}

Build and run:

$ javac -cp /vmsjavacallpath/vmscall.jar:/vmsjavacallpath/record.jar JF.java
$ java -cp .:/vmsjavacallpath/vmscall.jar:/vmsjavacallpath/record.jar JF

JF.groovy:

import java.text.*
import java.util.*

import dk.vajhoej.record.*

import static dk.vajhoej.vms.call.VMS.*

//--
// in integer arguments
//--
def test1() {
    a = 123
    b = 456
    stat = call("nfshr", "NADD", pass(new LongWord(a)).byReference().readOnly(),
                                 pass(new LongWord(b)).byReference().readOnly())
}
//--
// in character arguments
//--
def test2() {
    sa = "ABC"
    sb = "DEF"
    stat = call("nfshr", "NCONC", pass(new CharacterString(sa)).byDescriptor().readOnly(),
                                  pass(new CharacterString(sb)).byDescriptor().readOnly())
}
//--
// in and out integer arguments
//--
def test3() {
    a = 123
    b = 456
    cwrap = new LongWord()
    stat = call("nfshr", "NADDRET", pass(new LongWord(a)).byReference().readOnly(),
                                         pass(new LongWord(b)).byReference().readOnly(),
                                         pass(cwrap).byReference().writeOnly())
    c = cwrap.getValue()
    printf("Fortran gave : %d + %d = %d\n", a, b, c)
}
//--
// in and out character arguments
//--
def test4() {
    sa = "ABC"
    sb = "DEF"
    cswrap = new CharacterString(1024)
    stat = call("nfshr", "NCONCRET", pass(new CharacterString(sa)).byDescriptor().readOnly(),
                                          pass(new CharacterString(sb)).byDescriptor().readOnly(),
                                          pass(cswrap).byDescriptor().writeOnly())
    sc = cswrap.toString()
    printf("Fortran gave : %s + %s = %s\n", sa, sb, sc)
}
//--
// inout integer argument
//--
def test5() {
    v = 123
    vwrap = new LongWord(v)
    stat = call("nfshr", "NADD1", pass(vwrap).byReference().readWrite())
    v = vwrap.getValue()
    printf("Fortran gave : %d\n", v)
}
//--
// inout character argument
//--
def test6() {
    s = "ABC"
    swrap = new CharacterString(s, 1024)
    stat = call("nfshr", "NCONCX", pass(swrap).byDescriptor().readWrite())
    s = swrap.toString()
    printf("Fortran gave : %s\n", s)
}
//--
// inout record argument
//--
@Struct
class MyRec {
    static DateFormat df = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss")
    @StructField(n=0, type=FieldType.INT1)
    int b1
    @StructField(n=1, type=FieldType.INT1)
    int b2
    @StructField(n=2, type=FieldType.INT2)
    int w
    @StructField(n=3, type=FieldType.INT4)
    int lw
    @StructField(n=4, type=FieldType.VMSTIME)
    Date t
    @StructField(n=5, type=FieldType.FIXSTR, length=8, pad=true, padchar=' ')
    String s
    @Override
    String toString() {
        return String.format("(%d,%d,%d,%d,%s,%s)", b1, b2, w, lw, df.format(t), s)
    }
}
def test7() {
    r = new MyRec(b1: 1, b2: 2, w: 3, lw: 123, t: null, s: "ABC")
    rwrap = new Block(r)
    stat = call("nfshr", "NRECMOD", pass(rwrap).byReference().readWrite())
    r = (MyRec)rwrap.getObject(MyRec.class)
    printf("Fortran gave : %s\n", r)
}
test1()
test2()
test3()
test4()
test5()
test6()
test7()

Run:

$ groovy_cp = "/vmsjavacallpath/vmscall.jar:/vmsjavacallpath/record.jar"
$ groovy JF.groovy

Pascal:

Native side:

n.pas:

[inherit('sys$library:starlet')]
module n(input, output);

type
   BYTE = [byte] -128..127;
   WORD = [word] -32768..32767;
   pstr = varying [1024] of char;

function trim(s : pstr) : pstr;

var
   i : integer;

begin
   i := length(s);
   while (i > 0) and (s[i] = ' ') do i := i - 1;
   trim := substr(s, 1, i);
end;

(*--
in integer arguments
--*)
[global]
function nadd(a : integer; b : integer) : integer;

var
   c : integer;

begin
   c := a + b;
   writeln('Pascal says : ', a, ' + ', b, ' = ', c);
   nadd := 1;
end;

(*--
in character arguments
--*)
[global]
function nconc(sa : pstr; sb : pstr) : integer;

var
   sc : pstr;

begin
   sc := sa + sb;
   writeln('Pascal says : ', sa, ' + ', sb, ' = ', sc);
   nconc := 1;
end;

(*--
in and out integer arguments
--*)
[global]
function naddret(a : integer; b : integer; var c : integer) : integer;

begin
   c := a + b;
   naddret := 1;
end;

(*--
in and out character arguments
--*)
[global]
function nconcret(sa : pstr; sb : pstr; var sc : pstr) : integer;

begin
   sc := sa + sb;
   nconcret := 1;
end;

(*--
inout integer argument
--*)
[global]
function nadd1(var v : integer) : integer;

begin
   v := v + 1;
   nadd1 := 1;
end;

(*--
inout character argument
--*)
[global]
function nconcx(var s : pstr) : integer;

begin
   s := trim(s) + 'X';
   nconcx := 1;
end;

(*--
inout record argument
--*)
type
   myrec = record
              b1 : BYTE;
              b2 : BYTE;
              w : WORD;
              lw : integer;
              t : integer64;
              s : packed array [1..8] of char;
           end;

[global]
function nrecmod(var r : myrec) : integer;

begin
   r.b1 := r.b1 + 1;
   r.b2 := r.b2 + 1;
   r.w := r.w + 1;
   r.lw := r.lw + 1;
   $gettim(r.t);
   r.s := trim(r.s) + 'X';
   nrecmod := 1;
end;

end.

Build:

$ pas n
$ link/share=npshr n + sys$input/opt
SYMBOL_VECTOR=(NADD=PROCEDURE,-
               NCONC=PROCEDURE,-
               NADDRET=PROCEDURE,-
               NCONCRET=PROCEDURE,-
               NADD1=PROCEDURE,-
               NCONCX=PROCEDURE,-
               NRECMOD=PROCEDURE)
$
$ define/nolog npshr "''f$parse("npshr.exe")'"
$ exit

JVM side:

JP.java:

import java.io.UnsupportedEncodingException;
import java.text.DateFormat;;
import java.text.SimpleDateFormat;;
import java.util.Date;

import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.RecordException;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;

import static dk.vajhoej.vms.call.VMS.*;

public class JP {
    //--
    // in integer arguments
    //--
    private static void test1() {
        int a = 123;
        int b = 456;
        int stat = call("npshr", "NADD", pass(new LongWord(a)).byReference().readOnly(),
                                         pass(new LongWord(b)).byReference().readOnly());
    }
    //--
    // in character arguments
    //--
    private static void test2() throws UnsupportedEncodingException {
        String sa = "ABC";
        String sb = "DEF";
        int stat = call("npshr", "NCONC", pass(new VariableCharacterString(sa)).byReference().readOnly(),
                                          pass(new VariableCharacterString(sb)).byReference().readOnly());
    }
    //--
    // in and out integer arguments
    //--
    private static void test3() {
        int a = 123;
        int b = 456;
        LongWord cwrap = new LongWord();
        int stat = call("npshr", "NADDRET", pass(new LongWord(a)).byReference().readOnly(),
                                            pass(new LongWord(b)).byReference().readOnly(),
                                            pass(cwrap).byReference().writeOnly());
        int c = cwrap.getValue();
        System.out.printf("Pascal gave : %d + %d = %d\n", a, b, c);
    }
    //--
    // in and out character arguments
    //--
    private static void test4() throws UnsupportedEncodingException {
        String sa = "ABC";
        String sb = "DEF";
        VariableCharacterString cswrap = new VariableCharacterString(1024);
        int stat = call("npshr", "NCONCRET", pass(new VariableCharacterString(sa)).byReference().readOnly(),
                                             pass(new VariableCharacterString(sb)).byReference().readOnly(),
                                             pass(cswrap).byReference().writeOnly());
        String sc = cswrap.toString();
        System.out.printf("Pascal gave : %s + %s = %s\n", sa, sb, sc);
    }
    //--
    // inout integer argument
    //--
    private static void test5() {
        int v = 123;
        LongWord vwrap = new LongWord(v);
        int stat = call("npshr", "NADD1", pass(vwrap).byReference().readWrite());
        v = vwrap.getValue();
        System.out.printf("Pascal gave : %d\n", v);
    }
    //--
    // inout character argument
    //--
    private static void test6() throws UnsupportedEncodingException {
        String s = "ABC";
        VariableCharacterString swrap = new VariableCharacterString(s, 1024);
        int stat = call("npshr", "NCONCX", pass(swrap).byReference().readWrite());
        s = swrap.toString();
        System.out.printf("Pascal gave : %s\n", s);
    }
    //--
    // inout record argument
    //--
    private static DateFormat df = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
    @Struct
    public static class MyRec {
        @StructField(n=0, type=FieldType.INT1)
        private int b1;
        @StructField(n=1, type=FieldType.INT1)
        private int b2;
        @StructField(n=2, type=FieldType.INT2)
        private int w;
        @StructField(n=3, type=FieldType.INT4)
        private int lw;
        @StructField(n=4, type=FieldType.VMSTIME)
        private Date t;
        @StructField(n=5, type=FieldType.FIXSTR, length=8, pad=true, padchar=' ')
        private String s;
        public MyRec() {
            this(0, 0, 0, 0, null, null);
        }
        public MyRec(int b1, int b2, int w, int lw, Date t, String s) {
            this.b1 = b1;
            this.b2 = b2;
            this.w = w;
            this.lw = lw;
            this.t = t;
            this.s = s;
        }
        public int getB1() {
            return b1;
        }
        public void setB1(int b1) {
            this.b1 = b1;
        }
        public int getB2() {
            return b2;
        }
        public void setB2(int b2) {
            this.b2 = b2;
        }
        public int getW() {
            return w;
        }
        public void setW(int w) {
            this.w = w;
        }
        public int getLw() {
            return lw;
        }
        public void setLw(int lw) {
            this.lw = lw;
        }
        public Date getT() {
            return t;
        }
        public void setT(Date t) {
            this.t = t;
        }
        public String getS() {
            return s;
        }
        public void setS(String s) {
            this.s = s;
        }
        @Override
        public String toString() {
            return String.format("(%d,%d,%d,%d,%s,%s)", b1, b2, w, lw, df.format(t), s);
        }
    }
    @SuppressWarnings("unchecked")
    private static void test7() throws RecordException {
        MyRec r = new MyRec(1, 2, 3, 123, null, "ABC");
        Block rwrap = new Block(r);
        int stat = call("npshr", "NRECMOD", pass(rwrap).byReference().readWrite());
        r = (MyRec)rwrap.getObject(MyRec.class);
        System.out.printf("Pascal gave : %s\n", r);
    }
    public static void main(String[] args) throws UnsupportedEncodingException, RecordException {
        test1();
        test2();
        test3();
        test4();
        test5();
        test6();
        test7();
    }
}

Build and run:

$ javac -cp /vmsjavacallpath/vmscall.jar:/vmsjavacallpath/record.jar JP.java
$ java -cp .:/vmsjavacallpath/vmscall.jar:/vmsjavacallpath/record.jar JP

JP.groovy:

import java.text.*
import java.util.*

import dk.vajhoej.record.*

import static dk.vajhoej.vms.call.VMS.*

//--
// in integer arguments
//--
def test1() {
    a = 123
    b = 456
    stat = call("npshr", "NADD", pass(new LongWord(a)).byReference().readOnly(),
                                 pass(new LongWord(b)).byReference().readOnly())
}
//--
// in character arguments
//--
def test2() {
    sa = "ABC"
    sb = "DEF"
    stat = call("npshr", "NCONC", pass(new VariableCharacterString(sa)).byReference().readOnly(),
                                  pass(new VariableCharacterString(sb)).byReference().readOnly())
}
//--
// in and out integer arguments
//--
def test3() {
    a = 123
    b = 456
    cwrap = new LongWord()
    stat = call("npshr", "NADDRET", pass(new LongWord(a)).byReference().readOnly(),
                                         pass(new LongWord(b)).byReference().readOnly(),
                                         pass(cwrap).byReference().writeOnly())
    c = cwrap.getValue()
    printf("Pascal gave : %d + %d = %d\n", a, b, c)
}
//--
// in and out character arguments
//--
def test4() {
    sa = "ABC"
    sb = "DEF"
    cswrap = new VariableCharacterString(1024)
    stat = call("npshr", "NCONCRET", pass(new VariableCharacterString(sa)).byReference().readOnly(),
                                          pass(new VariableCharacterString(sb)).byReference().readOnly(),
                                          pass(cswrap).byReference().writeOnly())
    sc = cswrap.toString()
    printf("Pascal gave : %s + %s = %s\n", sa, sb, sc)
}
//--
// inout integer argument
//--
def test5() {
    v = 123
    vwrap = new LongWord(v)
    stat = call("npshr", "NADD1", pass(vwrap).byReference().readWrite())
    v = vwrap.getValue()
    printf("Pascal gave : %d\n", v)
}
//--
// inout character argument
//--
def test6() {
    s = "ABC"
    swrap = new VariableCharacterString(s, 1024)
    stat = call("npshr", "NCONCX", pass(swrap).byReference().readWrite())
    s = swrap.toString()
    printf("Pascal gave : %s\n", s)
}
//--
// inout record argument
//--
@Struct
class MyRec {
    static DateFormat df = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss")
    @StructField(n=0, type=FieldType.INT1)
    int b1
    @StructField(n=1, type=FieldType.INT1)
    int b2
    @StructField(n=2, type=FieldType.INT2)
    int w
    @StructField(n=3, type=FieldType.INT4)
    int lw
    @StructField(n=4, type=FieldType.VMSTIME)
    Date t
    @StructField(n=5, type=FieldType.FIXSTR, length=8, pad=true, padchar=' ')
    String s
    @Override
    String toString() {
        return String.format("(%d,%d,%d,%d,%s,%s)", b1, b2, w, lw, df.format(t), s)
    }
}
def test7() {
    r = new MyRec(b1: 1, b2: 2, w: 3, lw: 123, t: null, s: "ABC")
    rwrap = new Block(r)
    stat = call("npshr", "NRECMOD", pass(rwrap).byReference().readWrite())
    r = (MyRec)rwrap.getObject(MyRec.class)
    printf("Pascal gave : %s\n", r)
}
test1()
test2()
test3()
test4()
test5()
test6()
test7()

jp.com:

$ groovy_cp = "/vmsjavacallpath/vmscall.jar:/vmsjavacallpath/record.jar"
$ groovy JP.groovy

Basic:

Native side:

n.bas:

!--
! in integer arguments
!--
function integer nadd(integer a, integer b)
    declare integer c
    c = a + b
    print "Basic says : ", a, " + ", b, " = ", c
    nadd = 1
end function

!--
! in character arguments
!--
function integer nconc(string sa, string sb)
    declare string sc
    sc = sa + sb
    print "Basic says : ", sa, " + ", sb, " = ", sc
    nconc = 1
end function

!--
! in and out integer arguments
!--
function integer naddret(integer a, integer b, integer c)
    c = a + b    
    naddret = 1
end function

!--
! in and out character arguments
!--
function integer nconcret(string sa, string sb, string sc)
    sc = sa + sb
    nconcret = 1
end function

!--
! inout integer argument
!--
function integer nadd1(integer v)
    v = v + 1
    nadd1 = 1
end function

!--
! inout character argument
!--
function integer nconcx(string s)
    external string function trim(string)
    s = trim(s) + "X"
    nconcx = 1
end function

!--
! inout record argument
!--
function integer nrecmod(myrec r)
    record myrec
        byte b1
        byte b2
        word w    
        long lw
        quad t
        string s = 8
    end record
    external string function trim(string)
    external sub sys$gettim(quad)
    r::b1 = r::b1 + 1
    r::b2 = r::b2 + 1
    r::w = r::w + 1
    r::lw = r::lw + 1
    call sys$gettim(r::t)
    r::s = trim(r::s) + "X"
    nrecmod = 1
end function
!
function string trim(string s)
    declare integer ix
    ix = len(s)
    while ix > 1 and mid$(s, ix, 1) = " "
        ix = ix - 1
    next
    trim = mid$(s, 1, ix)
end function

Build:

$ bas n
$ link/share=nbshr n + sys$input/opt
SYMBOL_VECTOR=(NADD=PROCEDURE,-
               NCONC=PROCEDURE,-
               NADDRET=PROCEDURE,-
               NCONCRET=PROCEDURE,-
               NADD1=PROCEDURE,-
               NCONCX=PROCEDURE,-
               NRECMOD=PROCEDURE)
$
$ define/nolog nbshr "''f$parse("nbshr.exe")'"
$ exit

JVM side:

JB.java:

import java.io.UnsupportedEncodingException;
import java.text.DateFormat;;
import java.text.SimpleDateFormat;;
import java.util.Date;

import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.RecordException;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;

import static dk.vajhoej.vms.call.VMS.*;

public class JB {
    //--
    // in integer arguments
    //--
    private static void test1() {
        int a = 123;
        int b = 456;
        int stat = call("nbshr", "NADD", pass(new LongWord(a)).byReference().readOnly(),
                                         pass(new LongWord(b)).byReference().readOnly());
    }
    //--
    // in character arguments
    //--
    private static void test2() throws UnsupportedEncodingException {
        String sa = "ABC";
        String sb = "DEF";
        int stat = call("nbshr", "NCONC", pass(new CharacterString(sa)).byDescriptor().readOnly(),
                                          pass(new CharacterString(sb)).byDescriptor().readOnly());
    }
    //--
    // in and out integer arguments
    //--
    private static void test3() {
        int a = 123;
        int b = 456;
        LongWord cwrap = new LongWord();
        int stat = call("nbshr", "NADDRET", pass(new LongWord(a)).byReference().readOnly(),
                                            pass(new LongWord(b)).byReference().readOnly(),
                                            pass(cwrap).byReference().writeOnly());
        int c = cwrap.getValue();
        System.out.printf("Basic gave : %d + %d = %d\n", a, b, c);
    }
    //--
    // in and out character arguments
    //--
    private static void test4() throws UnsupportedEncodingException {
        String sa = "ABC";
        String sb = "DEF";
        CharacterString cswrap = new CharacterString(1024);
        int stat = call("nbshr", "NCONCRET", pass(new CharacterString(sa)).byDescriptor().readOnly(),
                                             pass(new CharacterString(sb)).byDescriptor().readOnly(),
                                             pass(cswrap).byDescriptor().writeOnly());
        String sc = cswrap.toString();
        System.out.printf("Basic gave : %s + %s = %s\n", sa, sb, sc);
    }
    //--
    // inout integer argument
    //--
    private static void test5() {
        int v = 123;
        LongWord vwrap = new LongWord(v);
        int stat = call("nbshr", "NADD1", pass(vwrap).byReference().readWrite());
        v = vwrap.getValue();
        System.out.printf("Basic gave : %d\n", v);
    }
    //--
    // inout character argument
    //--
    private static void test6() throws UnsupportedEncodingException {
        String s = "ABC";
        CharacterString swrap = new CharacterString(s, 1024);
        int stat = call("nbshr", "NCONCX", pass(swrap).byDescriptor().readWrite());
        s = swrap.toString();
        System.out.printf("Basic gave : %s\n", s);
    }
    //--
    // inout record argument
    //--
    private static DateFormat df = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
    @Struct
    public static class MyRec {
        @StructField(n=0, type=FieldType.INT1)
        private int b1;
        @StructField(n=1, type=FieldType.INT1)
        private int b2;
        @StructField(n=2, type=FieldType.INT2)
        private int w;
        @StructField(n=3, type=FieldType.INT4)
        private int lw;
        @StructField(n=4, type=FieldType.VMSTIME)
        private Date t;
        @StructField(n=5, type=FieldType.FIXSTR, length=8, pad=true, padchar=' ')
        private String s;
        public MyRec() {
            this(0, 0, 0, 0, null, null);
        }
        public MyRec(int b1, int b2, int w, int lw, Date t, String s) {
            this.b1 = b1;
            this.b2 = b2;
            this.w = w;
            this.lw = lw;
            this.t = t;
            this.s = s;
        }
        public int getB1() {
            return b1;
        }
        public void setB1(int b1) {
            this.b1 = b1;
        }
        public int getB2() {
            return b2;
        }
        public void setB2(int b2) {
            this.b2 = b2;
        }
        public int getW() {
            return w;
        }
        public void setW(int w) {
            this.w = w;
        }
        public int getLw() {
            return lw;
        }
        public void setLw(int lw) {
            this.lw = lw;
        }
        public Date getT() {
            return t;
        }
        public void setT(Date t) {
            this.t = t;
        }
        public String getS() {
            return s;
        }
        public void setS(String s) {
            this.s = s;
        }
        @Override
        public String toString() {
            return String.format("(%d,%d,%d,%d,%s,%s)", b1, b2, w, lw, df.format(t), s);
        }
    }
    @SuppressWarnings("unchecked")
    private static void test7() throws RecordException {
        MyRec r = new MyRec(1, 2, 3, 123, null, "ABC");
        Block rwrap = new Block(r);
        int stat = call("nbshr", "NRECMOD", pass(rwrap).byReference().readWrite());
        r = (MyRec)rwrap.getObject(MyRec.class);
        System.out.printf("Basic gave : %s\n", r);
    }
    public static void main(String[] args) throws UnsupportedEncodingException, RecordException {
        test1();
        test2();
        test3();
        test4();
        test5();
        test6();
        test7();
    }
}

Build and run:

$ javac -cp /vmsjavacallpath/vmscall.jar:/vmsjavacallpath/record.jar JB.java
$ java -cp .:/vmsjavacallpath/vmscall.jar:/vmsjavacallpath/record.jar JB

JB.groovy:

import java.text.*
import java.util.*

import dk.vajhoej.record.*

import static dk.vajhoej.vms.call.VMS.*

//--
// in integer arguments
//--
def test1() {
    a = 123
    b = 456
    stat = call("nbshr", "NADD", pass(new LongWord(a)).byReference().readOnly(),
                                 pass(new LongWord(b)).byReference().readOnly())
}
//--
// in character arguments
//--
def test2() {
    sa = "ABC"
    sb = "DEF"
    stat = call("nbshr", "NCONC", pass(new CharacterString(sa)).byDescriptor().readOnly(),
                                  pass(new CharacterString(sb)).byDescriptor().readOnly())
}
//--
// in and out integer arguments
//--
def test3() {
    a = 123
    b = 456
    cwrap = new LongWord()
    stat = call("nbshr", "NADDRET", pass(new LongWord(a)).byReference().readOnly(),
                                         pass(new LongWord(b)).byReference().readOnly(),
                                         pass(cwrap).byReference().writeOnly())
    c = cwrap.getValue()
    printf("Basic gave : %d + %d = %d\n", a, b, c)
}
//--
// in and out character arguments
//--
def test4() {
    sa = "ABC"
    sb = "DEF"
    cswrap = new CharacterString(1024)
    stat = call("nbshr", "NCONCRET", pass(new CharacterString(sa)).byDescriptor().readOnly(),
                                          pass(new CharacterString(sb)).byDescriptor().readOnly(),
                                          pass(cswrap).byDescriptor().writeOnly())
    sc = cswrap.toString()
    printf("Basic gave : %s + %s = %s\n", sa, sb, sc)
}
//--
// inout integer argument
//--
def test5() {
    v = 123
    vwrap = new LongWord(v)
    stat = call("nbshr", "NADD1", pass(vwrap).byReference().readWrite())
    v = vwrap.getValue()
    printf("Basic gave : %d\n", v)
}
//--
// inout character argument
//--
def test6() {
    s = "ABC"
    swrap = new CharacterString(s, 1024)
    stat = call("nbshr", "NCONCX", pass(swrap).byDescriptor().readWrite())
    s = swrap.toString()
    printf("Basic gave : %s\n", s)
}
//--
// inout record argument
//--
@Struct
class MyRec {
    static DateFormat df = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss")
    @StructField(n=0, type=FieldType.INT1)
    int b1
    @StructField(n=1, type=FieldType.INT1)
    int b2
    @StructField(n=2, type=FieldType.INT2)
    int w
    @StructField(n=3, type=FieldType.INT4)
    int lw
    @StructField(n=4, type=FieldType.VMSTIME)
    Date t
    @StructField(n=5, type=FieldType.FIXSTR, length=8, pad=true, padchar=' ')
    String s
    @Override
    String toString() {
        return String.format("(%d,%d,%d,%d,%s,%s)", b1, b2, w, lw, df.format(t), s)
    }
}
def test7() {
    r = new MyRec(b1: 1, b2: 2, w: 3, lw: 123, t: null, s: "ABC")
    rwrap = new Block(r)
    stat = call("nbshr", "NRECMOD", pass(rwrap).byReference().readWrite())
    r = (MyRec)rwrap.getObject(MyRec.class)
    printf("Basic gave : %s\n", r)
}
test1()
test2()
test3()
test4()
test5()
test6()
test7()

Run:

$ groovy_cp = "/vmsjavacallpath/vmscall.jar:/vmsjavacallpath/record.jar"
$ groovy JB.groovy

C:

Native side:

n.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

//--
// in integer arguments
//--
int nadd(int a, int b)
{
    int c = a + b;
    printf("C says : %d + %d = %d\n", a, b, c);
    return 1;
}

//--
// in character arguments
//--
int nconc(char *sa, char *sb)
{
    char *sc = malloc(strlen(sa) + strlen(sb) + 1);
    strcpy(sc, sa);
    strcat(sc, sb);
    printf("C says : %s + %s = %s\n", sa, sb, sc);
    free(sc);
    return 1;
}

//--
// in and out integer arguments
//--
int naddret(int a, int b, int *c)
{
    *c = a + b;
    return 1;
}

//--
// in and out character arguments
//--
int nconcret(char *sa, char *sb, char *sc)
{
    strcpy(sc, sa);
    strcat(sc, sa);
    return 1;
}

//--
// inout integer argument
//--
int nadd1(int *v)
{
    *v = *v + 1;
    return 1;
}

//--
// inout character argument
//--
int nconcx(char *s)
{
    char *p = s + strlen(s) - 1;
    while(*p == ' ')
    {
        *p = 0;
        p--;
    }
    strcat(s, "X");
    return 1;
}

//--
// inout record argument
//--
struct myrec
{
    char b1;
    char b2;
    short w;
    int lw;
    time_t t;
    char s[8];
};

int nrecmod(struct myrec *r)
{
    r->b1++;
    r->b2++;
    r->w++;
    r->lw++;
    r->t = time(NULL);
    strcat(r->s, "X");
    return 1;
}

Build and run:

$ cc n
$ link/share=ncshr n + sys$input/opt
SYMBOL_VECTOR=(NADD=PROCEDURE,-
               NCONC=PROCEDURE,-
               NADDRET=PROCEDURE,-
               NCONCRET=PROCEDURE,-
               NADD1=PROCEDURE,-
               NCONCX=PROCEDURE,-
               NRECMOD=PROCEDURE)
$
$ define/nolog ncshr "''f$parse("ncshr.exe")'"
$ exit

JVM side:

JC.java:

import java.io.UnsupportedEncodingException;
import java.text.DateFormat;;
import java.text.SimpleDateFormat;;
import java.util.Date;

import dk.vajhoej.record.FieldType;
import dk.vajhoej.record.RecordException;
import dk.vajhoej.record.Struct;
import dk.vajhoej.record.StructField;

import static dk.vajhoej.vms.call.VMS.*;

public class JC {
    //--
    // in integer arguments
    //--
    private static void test1() {
        int a = 123;
        int b = 456;
        int stat = call("ncshr", "NADD", pass(new LongWord(a)).byValue().readOnly(),
                                         pass(new LongWord(b)).byValue().readOnly());
    }
    //--
    // in character arguments
    //--
    private static void test2() throws UnsupportedEncodingException {
        String sa = "ABC";
        String sb = "DEF";
        int stat = call("ncshr", "NCONC", pass(new NullTermCharacterString(sa)).byReference().readOnly(),
                                          pass(new NullTermCharacterString(sb)).byReference().readOnly());
    }
    //--
    // in and out integer arguments
    //--
    private static void test3() {
        int a = 123;
        int b = 456;
        LongWord cwrap = new LongWord();
        int stat = call("ncshr", "NADDRET", pass(new LongWord(a)).byValue().readOnly(),
                                            pass(new LongWord(b)).byValue().readOnly(),
                                            pass(cwrap).byReference().writeOnly());
        int c = cwrap.getValue();
        System.out.printf("C gave : %d + %d = %d\n", a, b, c);
    }
    //--
    // in and out character arguments
    //--
    private static void test4() throws UnsupportedEncodingException {
        String sa = "ABC";
        String sb = "DEF";
        NullTermCharacterString cswrap = new NullTermCharacterString(1024);
        int stat = call("ncshr", "NCONCRET", pass(new NullTermCharacterString(sa)).byReference().readOnly(),
                                             pass(new NullTermCharacterString(sb)).byReference().readOnly(),
                                             pass(cswrap).byReference().writeOnly());
        String sc = cswrap.toString();
        System.out.printf("C gave : %s + %s = %s\n", sa, sb, sc);
    }
    //--
    // inout integer argument
    //--
    private static void test5() {
        int v = 123;
        LongWord vwrap = new LongWord(v);
        int stat = call("ncshr", "NADD1", pass(vwrap).byReference().readWrite());
        v = vwrap.getValue();
        System.out.printf("C gave : %d\n", v);
    }
    //--
    // inout character argument
    //--
    private static void test6() throws UnsupportedEncodingException {
        String s = "ABC";
        NullTermCharacterString swrap = new NullTermCharacterString(s, 1024);
        int stat = call("ncshr", "NCONCX", pass(swrap).byReference().readWrite());
        s = swrap.toString();
        System.out.printf("C gave : %s\n", s);
    }
    //--
    // inout record argument
    //--
    private static DateFormat df = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
    @Struct
    public static class MyRec {
        @StructField(n=0, type=FieldType.INT1)
        private int b1;
        @StructField(n=1, type=FieldType.INT1)
        private int b2;
        @StructField(n=2, type=FieldType.INT2)
        private int w;
        @StructField(n=3, type=FieldType.INT4)
        private int lw;
        @StructField(n=4, type=FieldType.UNIXTIME)
        private Date t;
        @StructField(n=5, type=FieldType.FIXSTRNULTERM, length=8)
        private String s;
        public MyRec() {
            this(0, 0, 0, 0, null, null);
        }
        public MyRec(int b1, int b2, int w, int lw, Date t, String s) {
            this.b1 = b1;
            this.b2 = b2;
            this.w = w;
            this.lw = lw;
            this.t = t;
            this.s = s;
        }
        public int getB1() {
            return b1;
        }
        public void setB1(int b1) {
            this.b1 = b1;
        }
        public int getB2() {
            return b2;
        }
        public void setB2(int b2) {
            this.b2 = b2;
        }
        public int getW() {
            return w;
        }
        public void setW(int w) {
            this.w = w;
        }
        public int getLw() {
            return lw;
        }
        public void setLw(int lw) {
            this.lw = lw;
        }
        public Date getT() {
            return t;
        }
        public void setT(Date t) {
            this.t = t;
        }
        public String getS() {
            return s;
        }
        public void setS(String s) {
            this.s = s;
        }
        @Override
        public String toString() {
            return String.format("(%d,%d,%d,%d,%s,%s)", b1, b2, w, lw, df.format(t), s);
        }
    }
    @SuppressWarnings("unchecked")
    private static void test7() throws RecordException {
        MyRec r = new MyRec(1, 2, 3, 123, null, "ABC");
        Block rwrap = new Block(r);
        int stat = call("ncshr", "NRECMOD", pass(rwrap).byReference().readWrite());
        r = (MyRec)rwrap.getObject(MyRec.class);
        System.out.printf("C gave : %s\n", r);
    }
    public static void main(String[] args) throws UnsupportedEncodingException, RecordException {
        test1();
        test2();
        test3();
        test4();
        test5();
        test6();
        test7();
    }
}

Build and run:

$ javac -cp /vmsjavacallpath/vmscall.jar:/vmsjavacallpath/record.jar JC.java
$ java -cp .:/vmsjavacallpath/vmscall.jar:/vmsjavacallpath/record.jar JC

JC.groovy:

import java.text.*
import java.util.*

import dk.vajhoej.record.*

import static dk.vajhoej.vms.call.VMS.*

//--
// in integer arguments
//--
def test1() {
    a = 123
    b = 456
    stat = call("ncshr", "NADD", pass(new LongWord(a)).byReference().readOnly(),
                                 pass(new LongWord(b)).byReference().readOnly())
}
//--
// in character arguments
//--
def test2() {
    sa = "ABC"
    sb = "DEF"
    stat = call("ncshr", "NCONC", pass(new NullTermCharacterString(sa)).byReference().readOnly(),
                                  pass(new NullTermCharacterString(sb)).byReference().readOnly())
}
//--
// in and out integer arguments
//--
def test3() {
    a = 123
    b = 456
    cwrap = new LongWord()
    stat = call("ncshr", "NADDRET", pass(new LongWord(a)).byReference().readOnly(),
                                         pass(new LongWord(b)).byReference().readOnly(),
                                         pass(cwrap).byReference().writeOnly())
    c = cwrap.getValue()
    printf("C gave : %d + %d = %d\n", a, b, c)
}
//--
// in and out character arguments
//--
def test4() {
    sa = "ABC"
    sb = "DEF"
    cswrap = new NullTermCharacterString(1024)
    stat = call("ncshr", "NCONCRET", pass(new NullTermCharacterString(sa)).byReference().readOnly(),
                                          pass(new NullTermCharacterString(sb)).byReference().readOnly(),
                                          pass(cswrap).byReference().writeOnly())
    sc = cswrap.toString()
    printf("C gave : %s + %s = %s\n", sa, sb, sc)
}
//--
// inout integer argument
//--
def test5() {
    v = 123
    vwrap = new LongWord(v)
    stat = call("ncshr", "NADD1", pass(vwrap).byReference().readWrite())
    v = vwrap.getValue()
    printf("C gave : %d\n", v)
}
//--
// inout character argument
//--
def test6() {
    s = "ABC"
    swrap = new NullTermCharacterString(s, 1024)
    stat = call("ncshr", "NCONCX", pass(swrap).byReference().readWrite())
    s = swrap.toString()
    printf("C gave : %s\n", s)
}
//--
// inout record argument
//--
@Struct
class MyRec {
    static DateFormat df = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss")
    @StructField(n=0, type=FieldType.INT1)
    int b1
    @StructField(n=1, type=FieldType.INT1)
    int b2
    @StructField(n=2, type=FieldType.INT2)
    int w
    @StructField(n=3, type=FieldType.INT4)
    int lw
    @StructField(n=4, type=FieldType.UNIXTIME)
    Date t
    @StructField(n=5, type=FieldType.FIXSTR, length=8, pad=true, padchar=' ')
    String s
    @Override
    String toString() {
        return String.format("(%d,%d,%d,%d,%s,%s)", b1, b2, w, lw, df.format(t), s)
    }
}
def test7() {
    r = new MyRec(b1: 1, b2: 2, w: 3, lw: 123, t: null, s: "ABC")
    rwrap = new Block(r)
    stat = call("ncshr", "NRECMOD", pass(rwrap).byReference().readWrite())
    r = (MyRec)rwrap.getObject(MyRec.class)
    printf("C gave : %s\n", r)
}
test1()
test2()
test3()
test4()
test5()
test6()
test7()

Run:

$ groovy_cp = "/vmsjavacallpath/vmscall.jar:/vmsjavacallpath/record.jar"
$ groovy JC.groovy

Article history:

Version Date Description
1.0 June 6th 2024 Initial version

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj