Extensions

SOMns aims to be a general purpose language that supports a wide range of use cases. Some applications might need to interact with the underlying system. For instance, one might want to open network sockets, access operating system functionality, or use Java libraries.

To avoid having to include all this functionality directly, SOMns offers a mechanism for extensions. Such extensions can provide functionality in Java and expose it to the language as normal Newspeak objects.

Typically, such extensions should be restricted to functionality that cannot otherwise be realized with SOMns.

An alternative to extensions would be Truffle's interop, which enables the use of other Truffle languages from within SOMns. However, SOMns' interop support is currently very limited.

General Design of Extension Modules

Extension modules can be loaded the same way as normal modules and present themselves as normal Newspeak classes. Thus, there is no visible difference between normal modules and extension modules from the language perspective.

Let's assume we have an extension that provides support for a database. It might be used as follows:

public loadExtensionAndConnect = (
  | (* load a .jar file instead of a .ns file *)
    moduleClass = system loadModule: 'database.jar'.
    (* instantiate the module class *)
    module = moduleClass new. |

  (* connect to database and perform a query *)
  module connectTo: 'localhost'.
  ^ module query: 'SELECT * FROM table'
)

To preserve the semantics of normal Newspeak classes, modules are isolated from each other and themselves. Thus, if an extension module is loaded multiple times, state that it maintains is kept separated.

Creating an Extension Module

Extension modules are realized as standard JAR files, which contain a properties file that identifies the class implementing the Extension interface, as well as a set of Truffle nodes annotated with @Primitive. The nodes are used to create a class, which offers them as normal methods.

As a simple example, let's implement a minimal counter primitive:

package ext;

@GenerateNodeFactory
@Primitive(primitive = "inc")
public class IncPrim extends UnaryExpressionNode {
  static long cnt = 0;

  @Specialization
  public Object inc(Object receiver) {
    cnt += 1;
    return cnt;
  }
}

The IncPrim node uses @Primitive to define a primitive method named inc. Its implementation simply increments a counter by one. Note that the node is a unary node, which means it takes one argument. In Newspeak, the first argument, i.e., the receiver is always present, but in this case it is simply ignored by the inc(.) method.

Note further the use of @GenerateNodeFactory. The annotation ensures that a factory class is generated, which we use when creating the module class and the methods corresponding to these primitives.

The second element we need is an implementation of Extension. In this example it is simply:

package ext;

public class Extension implements som.vm.Extension {
  @Override
  public List<Specializer<VM, ExpressionNode, SSymbol>> getSpecializers() {
    List<Specializer<VM, ExpressionNode, SSymbol>> specializers = new ArrayList<>();
    PrimitiveLoader.add(specializers, IncPrimFactory.getFactory());
    return specializers;
  }
}

An extension needs to provide a list of node factories, which are referring to node annotated with @Primitive.

The final element we need is the properties file. It needs to be named somns.extension and be placed in the root of the JAR. It defines which class provides the factories, and in this case it is:

class=ext.Extension

With the primitive nodes, the Extension class, and the somns.extension file, we have everything we need in the JAR file. To use it, as in the first example, we can use system loadModule: 'test.jar'.

Example Extension and Extension Structure

For the full implementation of the example see core-lib/TestSuite/extension.

The general recommended structure of such an extension could be something like the following:

build      # build directory, for jars and compiled classes
libs       # possible Java dependencies
src        # source directory for Java sources
.gitignore # ignore things like 'src_gen', which don't need to be checked into
           # the repository
build.xml  # Ant build script
src_gen    # for classes generated by the Truffle DSL
README.md  # general description and introduction to the extension

Additional Dependencies

As indicated, one reason to use SOMns extensions is to make Java libraries available to SOMns applications. Such libraries can depend themselves on other libraries. To express such dependencies, we rely on Java's support for JAR classpathes.

Dependencies therefore can be indicated with the Class-Path attribute of the META-INF/MANIFEST.MF file.

When assembling a JAR with Ant, the dependency could be added to the classpath for instance like this:

<jar destfile="${build.dir}/test-extension.jar" basedir="${classes.dir}">
  <manifest>
    <attribute name="Class-Path" value="libs/extension-dep.jar" />
  </manifest>
</jar>