Source code: nice/tools/code/SpecialArray.java
1 package nice.tools.code;
2
3 import gnu.bytecode.*;
4 import gnu.expr.*;
5
6 import bossa.util.Debug;
7
8 /**
9 Arrays that are wrapped on the fly into objects implementing java.util.List
10 when needed.
11 */
12
13 public class SpecialArray extends gnu.bytecode.ArrayType
14 {
15 /**
16 Return a SpecialArray holding elements of type <tt>elements</tt>.
17 */
18 public static Type create(Type elements)
19 {
20 String prefix;
21 if(elements instanceof PrimType)
22 prefix = elements.getName();
23 else
24 prefix = null;
25 Type res = Type.lookupType("[" + elements.getSignature());
26 if(res != null && res instanceof SpecialArray)
27 return res;
28
29 return new SpecialArray(elements, prefix, false, true);
30 }
31
32 public static SpecialArray unknownTypeArray()
33 {
34 if (unknownTypeArray == null)
35 unknownTypeArray = new SpecialArray(objectType, null, true, true);
36 return unknownTypeArray;
37 }
38
39 private static SpecialArray unknownTypeArray;
40
41 /** true if the elements have a primitive type. */
42 private boolean primitive;
43
44 /**
45 If true, this type denote array of elements with any type
46 (could be primitive or not).
47 So the signature for this is Object.
48 */
49 private boolean unknown;
50
51 protected SpecialArray (Type elements)
52 {
53 this(elements,
54 elements instanceof PrimType ? elements.getName() : null,
55 false, false);
56 }
57
58 private SpecialArray (Type elements, String prefix, boolean unknown,
59 boolean register)
60 {
61 super (elements);
62
63 this.unknown = unknown;
64 this.primitive = prefix != null;
65 this.prefix = prefix;
66
67 if (unknown)
68 // pretend we are java.lang.Object (for casts, method signatures...)
69 setSignature("Ljava/lang/Object;");
70
71 field = new Field(wrappedType);
72 field.setName("value");
73 field.setType(objectType);
74
75 if (register && !unknown)
76 {
77 Class c = elements.getReflectClass();
78 if (c == null)
79 bossa.util.Internal.warning("Null refclass for " + elements.getName());
80 else
81 {
82 if (c == Void.TYPE)
83 // It is illegal to construct an array of void values.
84 // This situation most probably originates in a bug
85 // in the source program, so let's notify it.
86 bossa.util.User.error(null, "Arrays cannot contain void values");
87
88 c = java.lang.reflect.Array.newInstance(c, 0).getClass();
89 Type.registerTypeForClass(c, this);
90 }
91
92 Type.registerTypeForName("[" + elements.getSignature(), this);
93 }
94 }
95
96 public String getInternalName()
97 {
98 if(unknown)
99 return "java.lang.Object";
100 else
101 return getSignature();
102 }
103
104 /****************************************************************
105 * Conversions
106 ****************************************************************/
107
108 // Only valid for primitive types.
109 private void callConvert(CodeAttr code)
110 {
111 if (convertMethod == null)
112 convertMethod = wrappedType.getDeclaredMethod("convert_" + prefix, 1);
113
114 code.emitInvokeStatic(convertMethod);
115 }
116
117 private void callGenericConvert(CodeAttr code)
118 {
119 if (genericConvertMethod == null)
120 if (primitive)
121 genericConvertMethod = wrappedType.getDeclaredMethod("gconvert_"+prefix, 1);
122 else
123 genericConvertMethod = wrappedType.getDeclaredMethod("gconvert", 2);
124
125 code.emitInvokeStatic(genericConvertMethod);
126 }
127
128 public void emitCoerceFrom (Type fromType, CodeAttr code)
129 {
130 if (fromType instanceof ArrayType)
131 {
132 // Any array can fit in the unknownTypeArray.
133 if (unknown)
134 return;
135
136 ArrayType from = (ArrayType) fromType;
137
138 // Do nothing either if both arrays are the same.
139 if (elements.getSignature().equals(from.elements.getSignature()))
140 return;
141
142 code.emitCheckcast(this);
143 }
144 else if (isCollectionArray(fromType))
145 emitCoerceFromCollection(code);
146 else
147 // The only possibility left is that the value is some kind of array.
148 emitCoerceFromArray(code);
149 }
150
151 /** @return true if the given type is one of the types an array can have
152 when considered as a collection.
153 */
154 private boolean isCollectionArray(Type t)
155 {
156 /* We list explicitely the type between Collection and Array.
157 If the hierarchy was changed, this code should be modified.
158 An alternative is to use
159 <code>t.isSubType(ClassType.make("java.util.Collection")) &&
160 wrappedType.isSubType(t)
161 </code>.
162 However it would only work if we ensured that the class hierarchy
163 at the gnu.bytecode level is correctly set up before any call
164 to this code. (Additionally it is less efficient)
165 */
166 String name = t.getName();
167 return
168 "java.util.Collection".equals(name) ||
169 "java.util.List".equals(name);
170 }
171
172 public void emitCoerceFromObject (CodeAttr code)
173 {
174 bossa.util.Internal.warning
175 ("SpecialArray.coerceFrom should probably be called instead of this");
176 emitCoerceFromArray(code);
177 }
178
179 private void emitCoerceFromCollection (CodeAttr code)
180 {
181 code.emitCheckcast(wrappedType);
182 code.emitGetField(field);
183
184 emitCoerceFromArray(code);
185 }
186
187 private void emitCoerceFromArray (CodeAttr code)
188 {
189 // If this array type is the unknown, we have nothing to do.
190 if (unknown)
191 return;
192
193 Label convert = new Label(code), end = new Label(code);
194
195 code.emitDup();
196 code.emitInstanceof(this);
197 code.emitGotoIfIntEqZero(convert);
198
199 code.emitCheckcast(this);
200 code.emitGoto(end);
201
202 convert.define(code);
203 code.emitCheckcast(objectArray);
204
205 if (primitive)
206 callConvert(code);
207 else
208 convertReferenceArray(code, this, elements);
209
210 end.define(code);
211 }
212
213 private static void convertReferenceArray
214 (CodeAttr code, ArrayType toType, Type elements)
215 {
216 code.emitDup();
217 code.emitIfNotNull();
218
219 code.emitDup();
220 code.emitArrayLength();
221
222 code.pushScope();
223 Variable len = code.addLocal(Type.int_type);
224 code.emitStore(len);
225
226 code.emitPushInt(0);
227
228 code.emitLoad(len);
229 code.emitNewArray(elements);
230
231 Variable dest = code.addLocal(toType);
232 code.emitDup();
233 code.emitStore(dest);
234
235 code.emitPushInt(0);
236
237 code.emitLoad(len);
238
239 code.emitInvokeStatic(arraycopy);
240 code.emitLoad(dest);
241 code.emitCheckcast(toType);
242
243 code.popScope();
244
245 code.emitElse();
246
247 code.emitPop(1);
248 code.emitPushNull(toType);
249
250 code.emitFi();
251 }
252
253 public static final Method arraycopy = ClassType.make("java.lang.System").
254 getDeclaredMethod("arraycopy", 5);
255
256 public void emitCoerceTo (Type toType, CodeAttr code)
257 {
258 if (toType instanceof ArrayType)
259 coerce(code, this, (ArrayType) toType);
260 else if (toType != objectType)
261 emitCoerceToObject(code);
262 }
263
264 public void emitCoerceToObject (CodeAttr code)
265 {
266 code.emitInvokeStatic(makeMethod);
267 }
268
269 private static void coerce (CodeAttr code, ArrayType from, ArrayType to)
270 {
271 // Any array can fit in the unknownTypeArray
272 if (to == unknownTypeArray)
273 return;
274
275 Type elements = to.getComponentType();
276 if (elements instanceof PrimType)
277 {
278 ((SpecialArray) SpecialArray.create(elements)).callGenericConvert(code);
279 }
280 else
281 {
282 boolean generic = ! from.getSignature().startsWith("[L");
283
284 if (generic)
285 {
286 // For generic arrays, we pass a string that represents
287 // the type of the elements, so that the correct array type
288 // can be created
289 code.emitPushString
290 (((ObjectType) elements).getInternalName().replace('/', '.'));
291
292 unknownTypeArray.callGenericConvert(code);
293
294 // Assert what we now guarantee.
295 code.emitCheckcast(to);
296 }
297 else
298 convertReferenceArray(code, to, elements);
299 }
300 }
301
302 /****************************************************************
303 * Typing
304 ****************************************************************/
305
306 public Type getImplementationType()
307 {
308 if (unknown)
309 return objectType;
310 else
311 return this;
312 }
313
314 public boolean isSubtype (Type other)
315 {
316 return other instanceof ArrayType &&
317 (other == unknownTypeArray ||
318 elements.isSubtype(((ArrayType) other).getComponentType()));
319 }
320
321 public boolean isAssignableTo (Type other)
322 {
323 return
324 other == objectType ||
325 other instanceof ArrayType &&
326 (other == unknownTypeArray ||
327 elements.isAssignableTo(((ArrayType) other).getComponentType()));
328 }
329
330 /****************************************************************
331 * Fields
332 ****************************************************************/
333
334 private static ClassType wrappedType = ClassType.make("nice.lang.rawArray");
335 public static ClassType wrappedType()
336 {
337 return wrappedType;
338 }
339
340 private Field field;
341
342 private static ClassType objectType = ClassType.make("java.lang.Object");
343 private static ArrayType objectArray = new ArrayType(objectType);
344
345 private Method convertMethod;
346 private Method genericConvertMethod;
347 private static Method makeMethod = wrappedType.getDeclaredMethod("make", 1);
348 private String prefix;
349
350 public String toString()
351 {
352 if (unknown)
353 return "Array with unknown element type";
354 else
355 return "SpecialArray(" + elements + ")";
356 }
357 }