Walnut/Ordinary Programming/Emakers
From Erights
Contents |
Library Packages: emakers
The E library package system is based on emakers. An emaker is an E source file named with ".emaker" as the suffix.
When an emaker is imported into an application, the code is executed at the time of import, and the result of that execution is returned to the importing program.
# E sample def makeQueue() { var head := null var tail := null def makeCarrier(obj) :near { var next := null def carrier { to attach(nextCarrier) {next := nextCarrier} to get() {return obj} to next() {return next} } } def queue { to push(obj) { if (head == null) { head := makeCarrier(obj) tail := head } else { tail.attach(makeCarrier(obj)) tail := tail.next() } } to pop() { def current := head head := head.next() return current.get() } to iterate(func) { var current := head var i := 0 while (current != null) { func(i, current.get()) current := current.next() i += 1 } } } return queue }
Side Note: This queue does have one interesting characteristic: the iterate method is the feature required to work correctly with E's for-loop. Iterate receives a function that expects 2 arguments: a key and an object. In this queue, the key value is filled with the index of the value in the queue. Iterate walks the entire data structure, invoking the function once for each element.
Where to put your emaker files
Place this queuemaker in a file named makeQueue.emaker. There are several ways of making E aware of the availability of an emaker, corresponding to the several ways Java can become aware of a class:
- You can import it using an ELoader, if you have a recent enough version of E.
- You can place it, or a jar that contains it, on the classpath when you start up the jvm, e.g. using java -cp libDir ... or rune -cpa libDir ....
- You can place it in a zip or a jar and place it in the jvm's ext directory.
- By placing the file in a subdirectory under lib/ext in the java directory system. You can use Java-style package specifications: as in Java, you can put the file in a jar in lib/ext. [ Should we be recommending this? Putting files in the Java installation itself seems like a recipe for conflicts between packages, and lost libraries when upgrading Java. ]
In the below example, we place the file under lib/ext/emakers/com/yourdomain/e/queueMaker.emaker (in your jre directory), and import:
# E syntax def makeQueue := <import:com.yourdomain.e.makeQueue> def queue := makeQueue() queue.push(1) queue.push(2) for each in queue {println(each)}
Security of emakers
Emakers have an important security property: they come to life contained in a world with no authority. Such a world is even more restrictive than the Java sandbox used for applets. However, this world is more flexible than the sandbox because authority can be granted and revoked during operation using capabilities, as described in the chapter on Secure Distributed Computing.
However, while emakers themselves are safe, adding a jar or directory to your classpath has a global effect on the JVM. For example, a jar may contain .safej files that grant emakers access to unsafe Java classes. Therefore you should use a custom loader if possible, rather than modifying the classpath. [ how? ]
The makeQueue function example is a nice first example of emakers in action because makeQueue and the queues it makes need no authority. For emakers that require authority, by convention we use the Authorizer pattern. Here is an infoWindowMakerAuthor: the infoWindowMaker needs the authority to create windows, so we wrap it in an Authorizer:
# E syntax #emaker in file lib/ext/com/skyhunter/infoWindowMakerAuthor.emaker def infoWindowMakerAuthor(frameMaker) { def infoWindowMaker(infoText) { def frame := frameMaker("Information") def label := <swing:makeJLabel>(infoText) frame.getContentPane().add(label) frame.pack() frame.show() return frame } return infoWindowMaker } #.....in using program.... def makeInfoWindow := <import:com.skyhunter.infoWindowMakerAuthor> (<swing:makeJFrame>) def infoBox := makeInfoWindow("First Bit Of Info") def detailedInfoBox := makeInfoWindow("Second Bit Of Info")
By convention, authors follow the pattern of functions, using an implied run() method to return the Maker that is being wrapped.
The meticulous reader may have noticed that the JFrame was a capability that needed to be authorized, whereas the JLabel was not. The vast bulk of the Java API is non-capability-transferring, and can just be acquired using import:. Commonly-used exceptions are the JFrame, everything have to do with files in java.io, and the URL object in java.net. For a complete listing of suppressed methods (which are inaccessible in E though documented in Java), safe classes (which can be imported directly by emakers), and unsafe classes (which can only be imported using the unsafe__uriGetter, which is not directly available to emakers), see the JavaDoc for E.
obsolete:Emakers can also just be functions, in which case the naming convention uses "Func" to distinguish it from a Maker:
# E syntax #emaker in file lib/ext/org/erights/computeSquareFunc.emaker def computeSquareFunc(number) { return number * number } #...in using program... def computeSquare := <import:org.erights.computeSquareFunc> def squareOfThree := computeSquare(3)
There are also functions that require capabilities before they can become operational, in which case they are by convention wrapped in Authorizers, for example, analyzeOutlineFuncAuthor.
The good news is that, because emakers come to life without authority, they can always be imported with import:, so any emaker can bring in any other emaker (though you won't be able to run the authorizer without sufficient authority). The bad news is that, if you are writing standalone E applications in which the emakers are fully trustworthy (or at least as trustworthy as the program that calls them), it can seem a substantial nuisance at first to be granting lists of authorities just to break your program into packages. If you are writing pure E applications that use emakers of your own construction, emakers which you trust totally with the full authority of the system, and you have no qualms about flouting the wisdom of architectural purists around the world, you can take a shortcut by simply granting the unsafe__uriGetter. For example:
# E sample def powerfulObjectMakerAuthor(unsafe__uriGetter) { def file := <unsafe:java.io.File>("name") def swing__uriGetter := <unsafe:javax.swing.*> def frame := <swing:makeJFrame>("powerful object window title") def powerfulObjectMaker() { #....define the objectMaker and the object it makes... } return powerfulObjectMaker() }
Note that in this example, one of the things we created from our unsafe__uriGetter was our own swing__uriGetter. Emakers, when they come to life, do have a swing__uriGetter of their own; however, their built-in swing: accesses only the safe subset of swing, much as import: accesses only the safe subset of unsafe:. In the example we create our own swing: that overrides the default swing:, which enables us access unsafe classes such as the JFrame.
For even more information on the security properties of emakers, see the chapter on Secure Mobile Code.
Exporting multiple objects
As importing an emaker returns the last value in the file, you can export multiple objects by ending with a map. For example:
# utils.emaker def foo(x) ... def bar(y) ... [ => foo, => bar ]
They can be used like this:
def <utils> := <import:utils> <utils:foo>("hello")
Note: <utils:foo> is syntactic sugar for <utils>["foo"].