1 /*
2  * Layout.java
3  *
4  * Copyright (C) 2003-2006 Peter Graves
5  * $Id$
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * As a special exception, the copyright holders of this library give you
22  * permission to link this library with independent modules to produce an
23  * executable, regardless of the license terms of these independent
24  * modules, and to copy and distribute the resulting executable under
25  * terms of your choice, provided that you also meet, for each linked
26  * independent module, the terms and conditions of the license of that
27  * module.  An independent module is a module which is not derived from
28  * or based on this library.  If you modify this library, you may extend
29  * this exception to your version of the library, but you are not
30  * obligated to do so.  If you do not wish to do so, delete this
31  * exception statement from your version.
32  */
33 
34 package org.armedbear.lisp;
35 
36 import java.util.concurrent.ConcurrentHashMap;
37 import static org.armedbear.lisp.Lisp.*;
38 
39 public class Layout extends LispObject
40 {
41   private final LispObject lispClass;
42   public final ConcurrentHashMap<LispObject, LispObject> slotTable;
43 
44   final LispObject[] slotNames;
45   final LispObject sharedSlots;
46 
47   private boolean invalid;
48 
Layout(LispObject lispClass, LispObject instanceSlots, LispObject sharedSlots)49   public Layout(LispObject lispClass, LispObject instanceSlots, LispObject sharedSlots)
50   {
51     this.lispClass = lispClass;
52     Debug.assertTrue(instanceSlots.listp());
53     int length = instanceSlots.length();
54     slotNames = new LispObject[length];
55     int i = 0;
56 
57     while (instanceSlots != NIL)
58       {
59         slotNames[i++] = instanceSlots.car();
60         instanceSlots = instanceSlots.cdr();
61       }
62 
63     Debug.assertTrue(i == length);
64     this.sharedSlots = sharedSlots;
65     slotTable = initializeSlotTable(slotNames);
66   }
67 
Layout(LispObject lispClass, LispObject[] instanceSlotNames, LispObject sharedSlots)68   public Layout(LispObject lispClass, LispObject[] instanceSlotNames,
69                 LispObject sharedSlots)
70   {
71     this.lispClass = lispClass;
72     this.slotNames = instanceSlotNames;
73     this.sharedSlots = sharedSlots;
74     slotTable = initializeSlotTable(slotNames);
75   }
76 
77   // Copy constructor.
Layout(Layout oldLayout)78   Layout(Layout oldLayout)
79   {
80     lispClass = oldLayout.getLispClass();
81     slotNames = oldLayout.slotNames;
82     sharedSlots = oldLayout.sharedSlots;
83     slotTable = initializeSlotTable(slotNames);
84   }
85 
initializeSlotTable(LispObject[] slotNames)86   private ConcurrentHashMap initializeSlotTable(LispObject[] slotNames)
87   {
88     ConcurrentHashMap ht = new ConcurrentHashMap(slotNames.length);
89     for (int i = slotNames.length; i-- > 0;)
90       ht.put(slotNames[i], Fixnum.getInstance(i));
91     return ht;
92   }
93 
94   @Override
getParts()95   public LispObject getParts()
96   {
97     LispObject result = NIL;
98     result = result.push(new Cons("class", getLispClass()));
99     for (int i = 0; i < slotNames.length; i++)
100       {
101         result = result.push(new Cons("slot " + i, slotNames[i]));
102       }
103     result = result.push(new Cons("shared slots", sharedSlots));
104     return result.nreverse();
105   }
106 
getLispClass()107   public LispObject getLispClass()
108   {
109     return lispClass;
110   }
111 
isInvalid()112   public boolean isInvalid()
113   {
114     return invalid;
115   }
116 
invalidate()117   public void invalidate()
118   {
119     invalid = true;
120   }
121 
getSlotNames()122   public LispObject[] getSlotNames()
123   {
124     return slotNames;
125   }
126 
getLength()127   public int getLength()
128   {
129     return slotNames.length;
130   }
131 
getSharedSlots()132   public LispObject getSharedSlots()
133   {
134     return sharedSlots;
135   }
136 
137   @Override
printObject()138   public String printObject()
139   {
140     return unreadableString("LAYOUT");
141   }
142 
143   // Generates a list of slot definitions for the slot names in this layout.
generateSlotDefinitions()144   protected LispObject generateSlotDefinitions()
145   {
146     LispObject list = NIL;
147     for (int i = slotNames.length; i-- > 0;)
148       list = list.push(new SlotDefinition(slotNames[i], NIL));
149 
150     return list;
151   }
152 
153   // ### make-layout
154   private static final Primitive MAKE_LAYOUT =
155     new Primitive("make-layout", PACKAGE_SYS, true,
156                   "class instance-slots class-slots")
157     {
158       @Override
159       public LispObject execute(LispObject first, LispObject second,
160                                 LispObject third)
161 
162       {
163           return new Layout(first, checkList(second), checkList(third));
164       }
165 
166     };
167 
168   // ### layout-class
169   private static final Primitive LAYOUT_CLASS =
170     new Primitive("layout-class", PACKAGE_SYS, true, "layout")
171     {
172       @Override
173       public LispObject execute(LispObject arg)
174       {
175           return checkLayout(arg).getLispClass();
176       }
177     };
178 
179   // ### layout-length
180   private static final Primitive LAYOUT_LENGTH =
181     new Primitive("layout-length", PACKAGE_SYS, true, "layout")
182     {
183       @Override
184       public LispObject execute(LispObject arg)
185       {
186           return Fixnum.getInstance(checkLayout(arg).slotNames.length);
187       }
188     };
189 
getSlotIndex(LispObject slotName)190   public int getSlotIndex(LispObject slotName)
191   {
192     LispObject index = slotTable.get(slotName);
193     if (index != null)
194       return ((Fixnum)index).value;
195     return -1;
196   }
197 
getSharedSlotLocation(LispObject slotName)198   public LispObject getSharedSlotLocation(LispObject slotName)
199 
200   {
201     LispObject rest = sharedSlots;
202     while (rest != NIL)
203       {
204         LispObject location = rest.car();
205         if (location.car() == slotName)
206           return location;
207         rest = rest.cdr();
208       }
209     return null;
210   }
211 
212   // ### layout-slot-index layout slot-name => index
213   private static final Primitive LAYOUT_SLOT_INDEX =
214     new Primitive("layout-slot-index", PACKAGE_SYS, true)
215     {
216       @Override
217       public LispObject execute(LispObject first, LispObject second)
218 
219       {
220           final LispObject slotNames[] = checkLayout(first).slotNames;
221           for (int i = slotNames.length; i-- > 0;)
222             {
223               if (slotNames[i] == second)
224                 return Fixnum.getInstance(i);
225             }
226           return NIL;
227       }
228     };
229 
230   // ### layout-slot-location layout slot-name => location
231   private static final Primitive LAYOUT_SLOT_LOCATION =
232     new Primitive("layout-slot-location", PACKAGE_SYS, true, "layout slot-name")
233     {
234       @Override
235       public LispObject execute(LispObject first, LispObject second)
236 
237       {
238             final Layout layOutFirst = checkLayout(first);
239             final LispObject slotNames[] = layOutFirst.slotNames;
240             final int limit = slotNames.length;
241             for (int i = 0; i < limit; i++)
242               {
243                 if (slotNames[i] == second)
244                   return Fixnum.getInstance(i);
245               }
246             // Reaching here, it's not an instance slot.
247             LispObject rest = layOutFirst.sharedSlots;
248             while (rest != NIL)
249               {
250                 LispObject location = rest.car();
251                 if (location.car() == second)
252                   return location;
253                 rest = rest.cdr();
254               }
255             return NIL;
256           }
257     };
258 
259   // ### %make-instances-obsolete class => class
260   private static final Primitive _MAKE_INSTANCES_OBSOLETE =
261     new Primitive("%make-instances-obsolete", PACKAGE_SYS, true, "class")
262     {
263       @Override
264       public LispObject execute(LispObject arg)
265       {
266         final LispObject lispClass = arg;
267         LispObject oldLayout;
268         // Non-finalized classes might not have a valid layout, but they do
269         // not have instances either so we can abort.
270         if (lispClass instanceof LispClass) {
271           if (!((LispClass)lispClass).isFinalized())
272             return arg;
273           oldLayout = ((LispClass)lispClass).getClassLayout();
274         } else if (lispClass instanceof StandardObject) {
275           if (((StandardObject)arg)
276               .getInstanceSlotValue(StandardClass.symFinalizedP) == NIL)
277             return arg;
278           oldLayout = Symbol.CLASS_LAYOUT.execute(lispClass);
279         } else {
280           return type_error(arg, Symbol.CLASS);
281         }
282 
283         Layout newLayout = new Layout((Layout)oldLayout);
284         if (lispClass instanceof LispClass)
285           ((LispClass)lispClass).setClassLayout(newLayout);
286         else
287           Symbol.CLASS_LAYOUT.getSymbolSetfFunction()
288               .execute(newLayout, lispClass);
289         ((Layout)oldLayout).invalidate();
290         return arg;
291       }
292     };
293 }
294