-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathJUICE.txt
809 lines (595 loc) · 35.7 KB
/
JUICE.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
Juice Authoring Tool
Introduction
Juice is a new technology for distributing executable content across the World Wide Web. In this respect, it
is similar to Java from Sun Microsystems. However, as we will try to explain in the following, Juice differs
from Java in several important aspects that allow it to outperform Java in many "downloadable Applets"
applications.
Juice is intended to be a complement to Java, giving users a choice: Java or Juice. Juice isn't simply a
"Java-lookalike" that we have created in quick reaction to Sun Microsystems' Java announcement. Quite to
the contrary, work on Juice has preceded Java by several years, but it all happened in an academic
environment with more limited resources than available at Sun.
Juice grew out of the Oberon research project headed by Niklaus Wirth, the inventor of the Pascal and
Modula programming languages. His aim was to invent a language that was at once simple and safe to use.
The new language should also support the object-oriented programming paradigm, be efficiently
compilable, and applicable to a wide range of programming tasks from systems programming to teaching.
A language with these properties was defined by Wirth in 1988 and given the name Oberon.
At first sight, Oberon and Java seem to be as different as two languages can possibly be. Oberon looks a lot
like Pascal (not very surprising, since they were both invented by the same person), and Java looks a lot
like C (thereby appearing "familiar" to many programmers). A closer comparison of Oberon and Java,
however, yields surprisingly many similarities: both languages
* are strongly typed while permitting the definition of "extended" types that are
backward-compatible with their ancestors,
* prohibit pointer arithmetic and mandate the presence of a garbage collector in
their run-time environment, and
* provide the notion of modular "packages" that are compiled separately and
dynamically linked at run-time
Juice is to the Oberon language what the Java Virtual Machine is to the Java language: the means of
distributing portable software. It encompasses three key components
* an architecture-neutral software distribution format,
* a compiler that translates from Oberon into the Slim Binary portable format, and
* a plug-in that enables the Netscape and Internet Explorer browser to run Juice applets
To the technical layman, the most notable difference between comparable Juice and Java applets is
probably that the Juice version of any applet is likely to run a lot faster than its Java counterpart. Rather
than being interpreted, as Java applets normally are, Juice always compiles each applet into the native code
of the target machine before it begins execution. Once that an applet begins executing, it runs at the full
speed of compiled code. Juice's on-the-fly compilation process is not only exceptionally fast, but it also
generates object code that is comparable in quality to commercial C compilers.
Further, the portable Slim Binary representation is denser than Java byte-codes. Users with slow
connections to the Internet save time by downloading Juice applets rather than Java applets of the same
functionality. In fact, the time saved by the faster downloading normally more than compensates for the on-
the-fly compilation phase.
How then, do a few academic researchers manage to come up with a better on-the-fly compiler than a
whole industry of software companies? Well, the key to Juice's effectiveness isn't really found in the
compiler technology embedded within the Netscape plug-in, but in the Juice software distribution format
itself: Behind the scenes, Juice and Java differ by far more than one might expect.
Java's distribution format is a sequence of so-called byte-codes: the inventors of Java thought up an "ideal"
processor designed specifically for running Java programs and use the instruction set of this ideal processor
as the "intermediate language" in which Java programs are shipped. Hence, on the programmer's side, a
Java compiler translates the source into a byte-code sequence, and on the side of the eventual consumer, a
byte-code interpreter simulates the ideal Java processor. It is entirely conceivable that the ideal Java
processor can actually be implemented in hardware, and Sun has actually already announced such
processors.
Juice's distribution format, on the other hand, is far more complex. It is based on a tree-shaped program
representation as is typically used transiently within optimizing compilers. Rather than containing a linear
code-sequence that can be interpreted byte-by-byte, a Juice-encoded applet contains a compressed tree that
describes the actions of the original program. The tree preserves the control-flow structure of the original
program, which makes it much easier to perform code optimization while the tree is translated into the
native instruction set of the target machine.
As an example of what kinds of optimizations may be beneficial, consider a modern processor that has
several functional units. These require a certain instruction-mix in order to operate at top speed. By re-
ordering certain mutually independent instructions, a better instruction-mix may be achieved. A tree-based
encoding maintains the notion of a basic block and makes it relatively easy to decide if two instructions are
mutually independent. This is not so simple with a linear instruction stream, such as Java byte-codes, in
which any instruction can potentially be the target of a branch.
Further, our tree-based encoding avoids many of the security issues that are difficult to solve in Java, or in
any virtual-machine representation for that matter. By the very definition of our tree-encoding scheme, it is
impossible to encode a tree that violates scoping rules of our source language. Such an "illegal" program
cannot even be constructed by hand. Java's byte-code instructions, on the other hand, are at a much lower
semantic level. Although it is possible to verify that a given byte-code sequence doesn't perform any illegal
action, this requires data-flow analysis and a partial repetition of the compiler's integrity checks. The tree-
based Juice distribution format in effect makes this particular data-flow analysis unnecessary. It also allows
for highly efficient load-time integrity checking, as every node in the encoded tree is fully typed.
Writing the First Applet
This chapter gives a first stepwise introduction in how to write your first simple "Hello World" applet and
how to execute it in the Oberon environment. If your are not already familiar with the Oberon system, we
strongly recommend that you first read the GettingStarted.Text to learn how to use the Oberon graphical
user interface and how to operate the compiler.
1) Write and Compile a New Applet
The first step on the way to a new applet is to write the source code and to compile it. You can either do
this by writing a new source file from scratch or by extending the skeleton module that is provided within
this version. The latter is much easier for beginners since the skeleton module already implements the basic
functionality that is needed for an average applet. Extending this module and tailoring it for your needs can
be done very easily and in a short period of time.
First, open the skeleton module in order to write a first "Hello World" applet. You can do this by executing
the following command.
Desktops.OpenDoc AppletSkeleton.Mod
You should see a new window that contains the AppletSkeleton module. Change the name of the document
to HelloWorldApplets.Mod and the name of the module to HelloWorldApplets. Then store the document
by clicking on the "Store" button. After having done this, add the following three, green marked lines to the
DoUpdate() procedure.
PROCEDURE DoUpdate (me: Applet);
BEGIN
(* Draw applet here. You me.device for drawing operations. *)
JuiceDevices.Setup(me.device);
JuiceDevices.String(20, 20, "Hello World!");
JuiceDevices.Restore(me.device)
END DoUpdate;
The DoUpdate() procedure is always called when the applet needs to be redrawn. Since we only want to
add code to write "Hello World!", the only statements that we need to insert is for setting up the graphics
device, for drawing the string at position (20, 20), and for restoring the graphics device.
Second, compile the source file with the Oberon compiler. Note, that there is no separate version of a Juice
compiler. Both the Juice and the Oberon compiler are one and the same for your convenience. Use the
following command to compile the module:
Compiler.Compile *\s
2) Write an Applet Description File (.adf)
You've just completed the first step - writing and compiling the applet source file. In a second step you
have to write an applet description file. Applet description files serve two purposes: First, they specify how
to create a new applet by defining a creator procedure (similar to an Oberon command) and second, they
specify parameters that are passed to the newly created applet. Applet description filenames always end
with the extension .adf.
Open a new HelloWorldApplet.adf file for editing by executing the following command:
Desktops.OpenDoc HelloWorldApplet.adf (TextDocs.NewDoc)
Note, that you cannot open the applet description file with a MR-interclick, as you normally do with text
files. You have to specify the document's creator in brackets. There are good reasons for this, as you will
see a little later.
Add the following line to the adf-file:
HelloWorldApplets.NewApplet
The first line in the adf-file always specifies the procedure that will be called when creating a new applet.
This procedure can have an arbitrary name but has to be implemented for proper execution of the applet.
Store the applet as soon as you have added the above line. Since not all applications that support Juice
applets (e.g. Netscape Navigator, MS Internet Explorer) have knowledge about the Oberon file-formats,
you have to store this file as an ASCII-text file. Choose either one of the following commands to do so.
EditTools.StoreAscii * if you are working under Windows 95
EditTools.StoreMac * if you are working under the MacOS
3) Executing an Applet
Executing an applet can easily be done by either executing the following command
Desktops.OpenDoc HelloWorldApplet.adf
or by an MR-interclick on the name HelloWorldApplet.adf. As you can see now, interclicking on the name
of the applet description file is used to run the applet. Since normally you will change the adf-file much
less than you will execute the source file, this solution is the most practical.
Adding an Applet to a HTML-Page
This chapter explains, how to integrate a Juice applet into a HTML-page. HTML-pages which contain
Juice-applets cannot only be used in Oberon, but also in Netscape Navigator and in Microsoft's Internet
Explorer. The latter two require that you download and install the Juice Netscape plug-in from our web-
server (http://www.ics.uci.edu/~juice).
The first step in creating a web-page that contains a Juice applet is to open a new text document.
Desktops.OpenDoc HelloWorld.html (TextDocs.NewDoc)
Now add the following lines to the document. They represent a very simple web page that displays the
string "This is a simple HelloWorld Juice-applet:" followed by the HelloWorld applet.
<HTML>
<HEAD> <TITLE>HelloWorld Juice Applet</TITLE> </HEAD>
<BODY>
This is a simple HelloWorld Juice-applet:
<EMBED SRC="HelloWorldApplet.adf" WIDTH=100 HEIGHT=100>
</BODY>
<HTML>
Juice applets are always inserted into a HTML page with the EMBED-tag. The EMBED-tag consists of the
<EMBED> keyword and one or more additional options. The most important options are given in the table
below.
SRC: Specifies the applet description file (.adf) that is used to create the new applet.
This option is mandatory for all Juice applets.
WIDTH: Specifies the width of the applet in screen pixels
HEIGHT: Specifies the height of the applet in screen pixels
ALIGN: Specifies alignment options (e.g. CENTER, TOP, LEFT, ...)
After having edited the HTML file, store it with one of the following commands.
EditTools.StoreAscii * if you are working under Windows 95
EditTools.StoreMac * if you are working under the MacOS
To see the result in Oberon, reopen the HTML-document with a MR-interclick on the name
HelloWorld.html or with the following command:
Desktops.OpenDoc HelloWorld.html
Alternatively, you can also drag the HelloWorld.html file onto Netscape Navigator or Microsoft's Internet
Explorer. However, both the HelloWorldApplet.adf-file and the object-file (HelloWorldApplets.Obj) have
to be located in the same directory as the HelloWorld.html-file. (This restriction is imposed by both Web-
browsers).
Important Notes:
1) As soon as you have finished developing the applet and the web-page you may want to copy all the files
to a web server for public use. However, this requires that your HTML-server sends the "juice/applet"
MIME-type along with the .adf file. If this is not the case, you will only see a broken icon instead of your
applet.
To solve this problem, consult your system administrator in order to associate the MIME-type
"juice/applet" to the filename-extension ".adf" in the HTML-server script.
2) When using the Oberon web browser to load applets from a remote server, Juice requires that a
directory with the name "Juice" exists in the Oberon root directory. The "Juice" directory is used to store
temporary files. Most of the time, the "Juice" directory seems to be empty, since temporary files are
removed as soon as possible. However, this directory may never be removed for the Juice Authoring Tool
to work properly.
The Juice Programming Interface
This chapter explains the Juice applet programming interface (API) in detail and may be used as a
reference for programming applets. As you will see soon, the API has been kept small and consistent for
your convenience so that you will always keep the overall view. However, you will probably criticize that
the Juice API is too small and lacks support for programming advanced graphical user interfaces. For our
defense, keep in mind that our primary goal was to bring a new technology to the Internet rather than
developing another large class-framework. Since one of the primary design goals of the Juice architecture
was extensibility, additional functionality can easily be provided by third-party developers.
1 Devices (Module JuiceDevices)
1.1 The Device
A Device is to Juice, what a GrafPort is to the MacOS, or a HDC (Handle to Device Context) to Windows
3.1 or Windows 95; the primary target for drawing operations. But unlike any other operating system, Juice
does not distinguish between printer ports and screen ports. The Juice architecture alone is responsible for
mapping the logical device to the actual physical device. This is very convenient since it does not add
unnecessary complexity to your programs.
Unlike Oberon the top left corner of the device is represented by the coordinates (0, 0) and the bottom right
corner by the coordinates (w-1, h-1). The color depth of the device (1, 2, 4, 8, 16, 32) is given in the field
depth, the physical horizontal and vertical resolution of the device in fields hres and vres.
TYPE
Device = POINTER TO DeviceDesc;
(* Graphics device, might be a screen, printer or offscreen device *)
DeviceDesc = RECORD
w, h: INTEGER; (* Width and height of device, top left ist always (0, 0) *)
depth: INTEGER; (* Device color depth *)
hres, vres: INTEGER; (* Horizontal and vertical resolution in pixel
per inch *)
END;
The most important thing that you have to know about devices is, that they have to be set up before
drawing, and that they have to be restored after drawing. Unfortunately this is a restriction imposed by the
Netscape plug-in architecture and cannot be avoided in any efficient way. So remember to always call
Setup() before drawing to a device and to always call Restore() after drawing to a device.
(* Setup the device for subsequent drawing calls - This procedure MUST be invoked before the
first drawing operation on a device *)
PROCEDURE Setup (D: Device);
(* Restore the device after previous drawing calls. This procedure MUST be invoked after the last
drawing operation on a device *)
PROCEDURE Restore (D: Device);
For efficiency reasons, you can also create additional offscreen-devices and copy blocks to the current
applet-device. Newly created offscreen devices are always initialized with white color.
(* Allocate a new offscreen device with width w, height h and given depth *)
PROCEDURE NewDevice (VAR D: Device; w, h, depth: INTEGER);
(* Copy block (sx, sy, w, h) from source device S to block (dx, dy, w, h) in destination device D
Note: The source device must not be the applet's device since it could be a printer device *)
PROCEDURE CopyBlock (S, D: Device; sx, sy, w, h, dx, dy: INTEGER);
CopyBlock(offscreen0, offscreen1, ...) ok
CopyBlock(offscreen0, applet.device, ...) ok
CopyBlock(applet.device, offscreen0, ...) NOT OK!!!
1.2 Clipping Masks
Using clipping masks for drawing can simplify many operations. For instance, one way to draw a half-
circle is to set the clipping mask to cover half the area where the circle will be drawn, then draw a full
circle. Basic clipping masks can either be created out of a rectangle (NewRectMask), an oval
(NewOvalMask), or a polygon (NewPolyMask). Complex clipping masks can be created by combining
basic or complex clipping masks by means of procedure DiffMask(), UnionMask(), and SectMask(). To
apply a clipping mask to a device, use the SetMask() procedure.
TYPE
Mask = POINTER TO MaskDesc; (* Clipping mask *)
Point = RECORD x, y: INTEGER END;
(* Create new clipping mask with a rectangular boundary x, y, w, h *)
PROCEDURE NewRectMask (VAR M: Mask; x, y, w, h: INTEGER);
(* Create new elliptic clipping mask with boundary x, y, w, h *)
PROCEDURE NewOvalMask (VAR M: Mask; x, y, w, h: INTEGER);
(* Create new clipping mask with a polyline boundary *)
PROCEDURE NewPolyMask (VAR M: Mask; VAR P: ARRAY OF Point; len: INTEGER);
(* Copy source mask S to destination mask D *)
PROCEDURE CopyMask (S: Mask; VAR D: Mask);
(* Calculate the difference D of source masks S0 and S1 *)
PROCEDURE DiffMask (S0, S1: Mask; VAR D: Mask);
(* Calculate the union D of source masks S0 and S1 *)
PROCEDURE UnionMask (S0, S1: Mask; VAR D: Mask);
(* Calculate the intersection D of source masks S0 and S1 *)
PROCEDURE SectMask (S0, S1: Mask; VAR D: Mask);
(* Set the clipping mask M for subsequent drawing operations *)
PROCEDURE SetMask (M: Mask);
1.3 Setting Drawing Attributes
In the Juice architecture, color information is always encoded as RGB-tuples, rather than as indexes to
color tables. This increases the degree of freedom for the underlying hardware to properly render graphics
primitives. The red, green, and blue value can range from 0 to 255. White is encoded as (255, 255, 255),
black as (0, 0, 0). Additionally, the most often used colors can be accessed directly by variables.
TYPE
RGBColor = RECORD (* RGB color, red, green and blue in [0..255] *)
red, green, blue: INTEGER
END;
VAR
white, black, red, green, blue: RGBColor; (* Default colors *)
Unlike in Oberon, drawing attributes (e.g. linewidth, drawing color, fontsize) are never passed to a
procedure as parameters. For efficiency reasons drawing attributes always have to be changed by calls to
special procedures. They remain valid for subsequent drawing primitives until they are changed by calling
the same procedure again with different parameters.
CONST
plainFace = 0; (* Plain font face *)
boldFace = 1; (* Bold font face *)
italicFace = 2; (* italic font face *)
(* Set foreground color for subsequent calls; default is black *)
PROCEDURE SetForeColor (col: RGBColor);
(* Set line width for subsequent calls; default is 1 *)
PROCEDURE SetLineWidth (width: INTEGER);
(* Set font for subsequent calls; default is "Times" *)
PROCEDURE SetFont (fnt: ARRAY OF CHAR);
(* Set font face (plainFace, italicFace, boldFace) for subsequent calls; default is plainFace *)
PROCEDURE SetFontFace (face: INTEGER);
(* Set font size for subsequent calls; default is 10 *)
PROCEDURE SetFontSize (size: INTEGER);
1.4 Drawing Primitives
The drawing primitives that are provided with the Juice API are very much alike the drawing primitives
that are used in the MacOS or in Windows 3.1 and Windows 95. As an Oberon programmer just remember
that the top-left corner has the coordinates (0, 0). Also note that the border of a framed object is always
included in the boundary, even if the frame is more than one pixels wide.
TYPE
Point = RECORD x, y: INTEGER END;
(* Draw dot at position (x, y) with given color *)
PROCEDURE Dot (x, y: INTEGER);
(* Draw a line from (x0, y0) to (x1, y1). The point (x1, y1) is included *)
PROCEDURE Line (x0, y0, x1, y1: INTEGER);
(* Draw a polyline with points P. Len contains the number of points *)
PROCEDURE FramePolyLine (VAR P: ARRAY OF Point; len: INTEGER);
(* Draw a filled polyline with points P. Len contains the number of points *)
PROCEDURE FillPolyLine (VAR P: ARRAY OF Point; len: INTEGER);
(* Draw a rectangle (x, y, w, h). The rectangle is inside the boundary, even if the linewidth
is greater than 0 *)
PROCEDURE FrameRect (x, y, w, h: INTEGER);
(* Draw a filled rectangle (x, y, w, h) *)
PROCEDURE FillRect (x, y, w, h: INTEGER);
(* Draw a circle or ellipse with boundary (x, y, w, h). The oval is inside the boundary, even if the
linewidth is greater than 0 *)
PROCEDURE FrameOval (x, y, w, h: INTEGER);
(* Draw a filled circle or ellipse with boundary (x, y, w, h) *)
PROCEDURE FillOval (x, y, w, h: INTEGER);
(* Draw a string s at position (x, y) with previously set color, fontname, fontsize and fontface *)
PROCEDURE String (x, y: INTEGER; s: ARRAY OF CHAR);
1.5 Font Metrics
Advanced operations for text-rendering require detailed information about font-metrics. This support is
provided by procedures GetFontMetrics() and GetStringSize(). While procedure GetFontMetrics() returns
information about the characteristics of a font, procedure GetStringSize() automatically calculates the
width and height of a given string.
TYPE
FontMetric = RECORD
ascent: INTEGER; (* Max distance above baseline *)
descent: INTEGER; (* Max distance below baseline *)
leading: INTEGER; (* Distance between lines *)
END;
(* Get metric data of current font *)
PROCEDURE GetFontMetrics (VAR metric: FontMetric);
(* Get width and height of given string s. Increment y by h to draw the next line *)
PROCEDURE GetStringSize (s: ARRAY OF CHAR; VAR w, h: INTEGER);
2 Files (Module JuiceFiles)
The purpose of module JuiceFiles is to implement the notion of a sequence of bytes stored on disk. Module
JuiceFiles exports the abstract data type File. A variable of type File identifies the data on the disk. For
security reasons, all files are located inside one flat filesystem.
TYPE
File = POINTER TO Handle;
Several operations (e.g. create file, delete file, ...) can be applied to files:
(* Creates a new file with the specified name. The same file descriptor is not returned with
multiple calls to New with the same filename (this results in multiple copies of a file with the
same name. i.e. the files are not registered in the directory). *)
PROCEDURE New (name: ARRAY OF CHAR): File;
(* Open an existing file. The same file descriptor is returned if a file is opened multiple times. *)
PROCEDURE Old (name: ARRAY OF CHAR): File;
(* Flushes the changes made to a file to disk. Register will automatically Close a file. *)
PROCEDURE Close (f: File);
(* Register a file created with New in the directory, replacing the previous file in the directory
with the same name. The file is automatically closed. *)
PROCEDURE Register (f: File);
(* Deletes a file. res = 0 indicates success. *)
PROCEDURE Delete (name: ARRAY OF CHAR; VAR res: INTEGER);
(* Renames a file. res = 0 indicates success. *)
PROCEDURE Rename (old, new: ARRAY OF CHAR; VAR res: INTEGER);
(* Deletes a file. *)
PROCEDURE Purge (f: File);
(* Returns length of file in bytes *)
PROCEDURE Length (f: File): LONGINT;
(* Returns the time (t) and date (d) of a file. *)
PROCEDURE GetDate (f: File; VAR t, d: LONGINT);
(* Returns the name of the file *)
PROCEDURE GetName (f: File; VAR name: ARRAY OF CHAR);
Module JuiceFiles also exports the abstract data type Rider. An instance of type Rider is associated with a
file and affords read/write access. It is the rider, not the file, which has the property position (the point
where read/write actions take place.)
TYPE
Rider = RECORD
res: LONGINT; (* result code *)
eof: BOOLEAN; (* end of file flag *)
END;
(* Positions a Rider at a certain position in a file. Multiple Riders can be positioned at different
locations in a file. A Rider cannot be positioned beyond the end of a file. *)
PROCEDURE Set (VAR r: Rider; f: File; pos: LONGINT);
(* Returns the File a Rider is based on. *)
PROCEDURE Base (VAR r: Rider): File;
(* Returns the offset of a Rider positioned on a file. *)
PROCEDURE Pos (VAR r: Rider): LONGINT;
(* Read operations. *)
PROCEDURE ReadChar (VAR R: Rider; VAR x: CHAR);
PROCEDURE ReadSInt (VAR R: Rider; VAR x: SHORTINT);
PROCEDURE ReadInt (VAR R: Rider; VAR x: INTEGER);
PROCEDURE ReadLInt (VAR R: Rider; VAR x: LONGINT);
PROCEDURE ReadSet (VAR R: Rider; VAR x: SET);
PROCEDURE ReadBool (VAR R: Rider; VAR x: BOOLEAN);
PROCEDURE ReadReal (VAR R: Rider; VAR x: REAL);
PROCEDURE ReadLReal (VAR R: Rider; VAR x: LONGREAL);
PROCEDURE ReadString (VAR R: Rider; VAR x: ARRAY OF CHAR);
PROCEDURE ReadNum (VAR R: Rider; VAR x: LONGINT);
(* Write operations. *)
PROCEDURE WriteChar (VAR R: Rider; x: CHAR);
PROCEDURE WriteSInt (VAR R: Rider; x: SHORTINT);
PROCEDURE WriteInt (VAR R: Rider; x: INTEGER);
PROCEDURE WriteLInt (VAR R: Rider; x: LONGINT);
PROCEDURE WriteSet (VAR R: Rider; x: SET);
PROCEDURE WriteBool (VAR R: Rider; x: BOOLEAN);
PROCEDURE WriteReal (VAR R: Rider; x: REAL);
PROCEDURE WriteLReal (VAR R: Rider; x: LONGREAL);
PROCEDURE WriteString (VAR R: Rider; x: ARRAY OF CHAR);
PROCEDURE WriteNum (VAR R: Rider; x: LONGINT);
A special means is provided to access files over the World-Wide-Web. Since file transfers in Netscape and
Microsoft's Internet Explorer are asynchronous, a notifier procedure (also called callback-function) has to
be passed as a parameter to the RequestURL() procedure. This notifier procedure is called as soon as the
file has successfully been transfered. The data parameter in RequestURL() is directly passed to the notifier
procedure.
TYPE
Notifier = PROCEDURE (F: File; data: PTR);
(* Start transfering the file with given URL and call notify after transfering the file. *)
PROCEDURE RequestURL (name: ARRAY OF CHAR; notify: Notifier; data: PTR);
An example on how to use this mechanism can be found in the Sisiphus applet:
TYPE
Data = POINTER TO RECORD name: ARRAY 64 OF CHAR END;
VAR
data: Data;
PROCEDURE LoadFig (F: JuiceFiles.File; data: PTR); (* read file *)
BEGIN
(* since data transfer is asynchronous, file "world" might arrive before file "icon" *)
WITH data: Data DO
IF data.name = "Sisiphus" THEN (* load icon from F *)
ELSIF data.name = "World" THEN (* load world from F *)
END
END
END LoadFig;
BEGIN (* module *)
(* request file Sisiphus.Icon from Server and call LoadFig when loaded *)
NEW(data); data.name := "Sisphus";
JuiceFiles.RequestURL("Sisiphus.Icon", LoadFig, data);
(* request file World.Icon from Server and call LoadFig when loaded *)
NEW(data); data.name := "World";
JuiceFiles.RequestURL("http://ics.uci.edu/~juice/World.Icon", LoadFig, data);
END Sisiphus;
3 Applets (Module JuiceApplets)
3.1 Applets
The base type of all Juice applets is Applet. Every applet has its predefined device that is allocated and set
by the Juice architecture. You must not change the applet's device by your own!
Applet = POINTER TO AppletDesc;
AppletDesc = RECORD
handle: Handler; (* message handler *)
device-: JuiceDevices.Device; (* Applet's device to draw to / read-only *)
END;
VAR
newApplet: Applet; (* Newly generated applets are returned here *)
Procedures that are assigned to the field handle are termed handlers. The handler of every applet is
responsible for processing incoming messages. In other words, sending a message to an applet is done by
calling its handler procedure with the message as actual parameter. Handlers are of the following type.
TYPE
Handler = PROCEDURE (applet: Applet; VAR M: AppletMsg);
PROCEDURE AppletHandler (me: Applet; VAR M: AppletMsg);
Procedure AppletHandler() is a predefined handler that "understands" the most important messages.
Whenever your applet does not handle incoming messages these messages should be forwarded to this
procedure by calling
JuiceApplets.AppletHandler(me, M).
Generating a new applet is quite easy. However you have to meet at least the following requirements: a
new applet has to be allocated, the message handler has to be installed, and the newly created object has to
be assigned to the variable JuiceApplets.newApplet. If you forget to do one of these things, your applet
won't work!
PROCEDURE NewApplet;
VAR a: MyApplet;
BEGIN
NEW(a); (* Generate new applet *)
a.handle := MyAppletHandler; (* Install message handler *)
JuiceApplets.newApplet := a
(* Assign newly created applet to JuiceApplets.newApplet *)
END NewApplet;
one of these things, your applet won't work!
3.2 Applet Messages
A message serves to communicate information from one applet to another, or from the host-application to
the applet. Messages are always passed as a parameter to a handler. The base type of all messages is
AppletMsg. Various other messages are extended from this type.
CONST
update* = 0; resize* = 1; (* DisplayMsg modes *)
get = 0; lose = 1; (* FocusMsg modes *)
TYPE
(* Base type of all messages sent to applets. *)
AppletMsg = RECORD END;
(* Applets receive mouse messages to respond to mouse movement and button clicks. *)
MouseMsg = RECORD( AppletMsg )
x, y: INTEGER; (* x, y position of mouse *)
buttons: SET (* keys pressed {JuiceDevices.leftButton,
JuiceDevices.middleButton, JuiceDevices.rightButton} *)
END;
(* Applets receive keyboard messages to respond to keyboard input. *)
KeyboardMsg = RECORD( AppletMsg )
ch: CHAR (* key pressed *)
END;
(* Applets receive display messages to redraw themselves. *)
DisplayMsg = RECORD( AppletMsg )
id: SHORTINT; (* update, resize *)
END;
(* Applets are notified when they get or loose the focus *)
FocusMsg = RECORD( AppletMsg )
id: SHORTINT; (* get, loose *)
END;
(* An init message is sent to every applet after it has been allocated.
Here, you should initialize the device-space your applet uses by filling it with a
default color. This is not done automatically by the Juice architecture, so that non-
rectangular applets take on the background of the web page they reside in. *)
InitMsg = RECORD( AppletMsg ) END;
(* A destroy message is sent to every applet before reclaiming its storage. *)
DestroyMsg = RECORD( AppletMsg ) END;
(* Idle messages are sent to applets at fixed time intervals to facilitate background
tasks. *)
IdleMsg = RECORD( AppletMsg ) END;
The following procedure is a simple example handler that reacts to the display and keyboard message.
Examples on how to write advanced message handlers can be found in the enclosed example modules (e.g.
ClockApplets.Mod, SisiphusApplets.Mod, WireframeApplets.Mod).
PROCEDURE AppletHandler (me: JuiceApplets.Applet; VAR M: JuiceApplets.AppletMsg);
BEGIN
WITH me: MyApplet DO
IF M IS JuiceApplets.DisplayMsg THEN
WITH M: JuiceApplets.DisplayMsg DO
IF M.id = JuiceApplets.update THEN
(* just refresh applet *)
ELSIF M.id = JuiceApplets.resize THEN
(* resize and redraw applet *)
ELSE
(* cannot handle message *)
JuiceApplets.AppletHandler(me, M)
END
END
ELSIF M IS JuiceApplets.KeyboardMsg THEN
WITH M: JuiceApplets.KeyboardMsg DO
(* process keyboard input *)
END
ELSE
(* cannot handle message *)
JuiceApplets.AppletHandler(me, M)
END
END
END AppletHandler;
Instead of sending a message to one specific applet, you can also broadcast a message to all visible applets
by means of
(* Send message M to all currently visible applets. *)
PROCEDURE Broadcast (VAR M: AppletMsg);
3.3 Reading Parameters
Module JuiceApplets also provides functionality to read parameters that are specified in the applet
description file.
CONST
(* Parameter symbol classes.*)
Inval = 0; (* Invalid symbol. *)
String = 2; (* string s (length len). *)
Int = 3; (* Integer i (decimal or hexadecimal). *)
Real = 4; (* Real number x. *)
LongReal = 5; (* Long real number y. *)
Char = 6; (* Special character c. *)
NotFound = 7; (* Parameter not found *)
TYPE
Param = RECORD
class: INTEGER; (* Scan result: Int, Real, String etc. *)
i: LONGINT; (* Integer value *)
x: REAL; (* Real value *)
y: LONGREAL; (* Longreal value *)
c: CHAR; (* Character value *)
s: ARRAY 256 OF CHAR; (* String value *)
len: SHORTINT; (* Length of name or string scanned. *)
END;
PROCEDURE GetParam (me: Applet; name: ARRAY OF CHAR; VAR value: Param);
The following extract of a program gives an example on how to read parameters:
JuiceApplets.GetParam(me, "type", par);
IF (par.class = JuiceApplets.String) & (par.s = "digital") THEN digital := TRUE ELSE digital :=
FALSE END;
JuiceApplets.GetParam(me, "size", par);
IF (par.class = JuiceApplets.Int) THEN size := par.i ELSE size := predefSize END;
JuiceApplets.GetParam(me, "ratio", par);
IF (par.class = JuiceApplets.Real) THEN ratio := par.x ELSE ratio := predefRatio END;
It would yield the variable configuration (digital = TRUE; size = predefSize; ratio = 1.5) after reading the
following .adf file.
MyApplet.NewApplet
ratio = 1.5
type = digital
4 Mathematical Functions (Module JuiceMath and JuiceMathL)
Two mathematical libraries are provided by means of module JuiceMath which operates on REAL-
numbers, and module JuiceMathL which operates on LONGREAL-numbers.
CONST
e= 2.7182817E+00;
pi= 3.1415927E+00;
PROCEDURE Arctan (x: REAL): REAL;
PROCEDURE Cos (x: REAL): REAL;
PROCEDURE Exp (x: REAL): REAL;
PROCEDURE Ln (x: REAL): REAL;
PROCEDURE Sin (x: REAL): REAL;
PROCEDURE Sqrt (x: REAL): REAL;
CONST
e= 2.71828182845905D+000;
pi= 3.14159265358979D+000;
PROCEDURE Arctan (x: LONGREAL): LONGREAL;
PROCEDURE Cos (x: LONGREAL): LONGREAL;
PROCEDURE Exp (x: LONGREAL): LONGREAL;
PROCEDURE Ln (x: LONGREAL): LONGREAL;
PROCEDURE Sin (x: LONGREAL): LONGREAL;
PROCEDURE Sqrt (x: LONGREAL): LONGREAL;
5 Miscellaneous Procedures (Module JuiceMisc)
Module JuiceMisc implements two functions to determine the current date and time:
(* Return the current time in hours, minutes and seconds. *)
PROCEDURE GetTime (VAR hour, min, sec: INTEGER);
(* Return the current date in days, months and years. *)
PROCEDURE GetDate (VAR day, month, year: INTEGER);
In future versions of Juice, this module is very likely to be extended by additional procedures.
Conclusion
We believe that Juice is a better technology than Java for bringing executable content to the World Wide
Web. Juice allows substantial amounts of software to be encoded space-efficiently, and to be compiled on-
the-fly into high-quality code. The Juice architecture-neutral distribution format has a variety of properties
that make it inherently better suited for applications that require the speed of optimized object code.
University of California, Irvine
Michael Franz and Thomas Kistler
22th October 1996
"Java" is a trademark owned by Sun Microsystems, Inc.
"Juice" is a trademark owned by the Regents of the University of California
"Slim Binaries" is a trademark owned by the Regents of the University of California