KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openlaszlo > sc > Assembler


1 /***
2  * Assembler.java
3  *
4  * Description: The assembler translates an assembly instruction into
5  * a sequence of bytes.
6  */

7
8 /* J_LZ_COPYRIGHT_BEGIN *******************************************************
9 * Copyright 2001-2004 Laszlo Systems, Inc. All Rights Reserved. *
10 * Use is subject to license terms. *
11 * J_LZ_COPYRIGHT_END *********************************************************/

12
13 package org.openlaszlo.sc;
14 import java.io.*;
15 import java.nio.*;
16 import java.util.*;
17 import org.openlaszlo.sc.Instructions.*;
18 import org.openlaszlo.sc.Emitter;
19
20 // Assembly is relatively straightforward except for the PUSH
21
// instruction, and label resolution.
22
//
23
// During the assembly of PUSH, strings in the constant pool are
24
// replaced by constant-pool references.
25
//
26
// Note that the assembler, not the code generator, is responsible for
27
// recognizing which string arguments to a PUSH instruction can refer
28
// to the constant pool.
29
//
30
// Label targets are are entered in a table mapping label names to
31
// offsets. Instructions that contain label sources are processed as
32
// follows: a backwards reference is replaced by a byte offset, while a
33
// forward-reference is entered in a table mapping a label name to a
34
// list of offsets whence the label is referenced. When a label is
35
// encountered, the forward-reference table is used to backpatch
36
// forward references.
37

38 public class Assembler implements Emitter {
39   Hashtable labels;
40   ByteBuffer bytes;
41   private static byte[] backingStore;
42   Hashtable constants;
43
44   public static class Label {
45     Object JavaDoc name;
46     ByteBuffer bytes;
47     int location;
48     List references;
49
50     public Label(Object JavaDoc name) {
51       this.name = name;
52       this.location = -1;
53       this.references = new ArrayList();
54     }
55
56     public boolean isResolved() {
57       return this.location != -1;
58     }
59
60     public void setLocation(ByteBuffer bytes) {
61       assert (! this.isResolved()) : "Label.setLocation() called on resolved label";
62       this.location = bytes.position();
63       // Backpatch forward jumps
64
for (Iterator i = this.references.iterator(); i.hasNext(); ) {
65         int patchloc = ((Integer JavaDoc)i.next()).intValue();
66         int offset = location - patchloc - 2;
67         if (offset != (short)offset) {
68             // throw new CompilerException("Jump offset too large");
69
}
70         bytes.putShort(patchloc, (short)offset);
71       }
72       this.references = null;
73     }
74
75     public void addReference(int patchloc) {
76       assert (! this.isResolved()) : "adding reference to resolved label";
77       this.references.add(new Integer JavaDoc(patchloc));
78     }
79
80     public String JavaDoc toString() {
81       return this.name.toString();
82     }
83   }
84
85   private synchronized byte[] getBacking() {
86     byte[] b = backingStore;
87     backingStore = null;
88     return b;
89   }
90
91   private synchronized void setBacking(byte[] b) {
92     backingStore = b;
93   }
94
95   public Assembler() {
96     this.labels = new Hashtable(); // {String -> Label}
97
// Try to reuse the backing buffer
98
byte[] bs = getBacking();
99     if (bs != null) {
100       this.bytes = ByteBuffer.wrap(bs);
101     } else {
102       // Room to not grow immediately. See emit.
103
this.bytes = ByteBuffer.allocate((1<<14) + (1<<16));
104     }
105     this.constants = new Hashtable(); // {String -> int}
106
}
107
108   public byte[] assemble(List instrs) {
109     for (Iterator i = instrs.iterator(); i.hasNext(); ) {
110       this.emit((Instruction)i.next());
111     }
112     List unresolvedLabels = new ArrayList();
113     // One wonders why this couldn't be an Iterator
114
for (Enumeration i = this.labels.elements(); i.hasMoreElements(); ) {
115       Label label = (Label)i.nextElement();
116       if (! label.isResolved()) {
117         unresolvedLabels.add(label);
118       }
119     }
120     //System.out.println("assembled: " + this.bytes.toString());
121
assert (unresolvedLabels.size() == 0) : "unresolved labels: " + unresolvedLabels;
122     // TODO [2004-03-04 ptw] be more efficient than this!
123
byte[] result = new byte[this.bytes.position()];
124     this.bytes.flip();
125     this.bytes.get(result);
126     // Save the backing buffer
127
setBacking(bytes.array());
128     return result;
129   }
130
131   public Label getLabel(Object JavaDoc name) {
132     if (! this.labels.containsKey(name)) {
133       this.labels.put(name, new Label(name));
134     }
135     return (Label)this.labels.get(name);
136   }
137
138   public void emit(Instruction instr) {
139     // Verify there is room for a maximal instruction (1<<16)
140
// TODO: [2004-08-02 ptw] 1<<16 does not work. Why?
141
// As a temporary kludge, double that.
142
if (! (bytes.remaining() > 1<<18)) {
143       // TODO [2004-03-11 ptw] Spool to file above a certain size
144
ByteBuffer newBytes = ByteBuffer.allocate(bytes.capacity() * 2);
145       ByteBuffer oldBytes = bytes;
146       bytes.flip();
147       newBytes.put(bytes);
148       bytes = newBytes;
149       //System.out.println("Grow buffer: " + oldBytes + " => " + newBytes);
150
}
151     if (instr instanceof ConcreteInstruction && ((ConcreteInstruction)instr).op == Actions.CONSTANTS) {
152       // Initialize the constant map
153
this.constants = new Hashtable();
154       for (ListIterator i = ((ConcreteInstruction)instr).args.listIterator(); i.hasNext(); ) {
155         int index = i.nextIndex();
156         this.constants.put(i.next(), new Integer JavaDoc(index));
157       }
158       // Fall through to the general case
159
}
160     if (instr instanceof LABELInstruction) {
161       Object JavaDoc name = ((LABELInstruction)instr).name;
162       Label label = this.getLabel(name);
163       assert (! label.isResolved()) : "duplicate label" + label;
164       // Get the current location, and save it for backjumps
165
label.setLocation(bytes);
166     } else if (instr instanceof TargetInstruction) {
167       TargetInstruction target = (TargetInstruction)instr;
168       Label label = this.getLabel(target.getTarget());
169       int loc = label.location;
170       if (loc == -1) {
171         // Target location isn't yet available. Use a null
172
// offset, and add the address to be patched to this
173
// label's list of backpatch locations.
174
target.writeBytes(this.bytes, this.constants);
175         int patchloc = bytes.position() - 2;
176         label.addReference(patchloc);
177       } else {
178         // Target computation requires that we write the instruction first!
179
target.targetOffset = 0;
180         target.writeBytes(this.bytes, this.constants);
181         int offset = loc - bytes.position();
182         if (offset != (short)offset) {
183             throw new CompilerException("Jump offset too large");
184         }
185         bytes.putShort(bytes.position() - 2, (short)offset);
186         }
187     } else {
188       instr.writeBytes(this.bytes, this.constants);
189     }
190   }
191 }
192
Popular Tags