Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 }