source: branches/1.1.x/src/org/armedbear/lisp/StandardObject.java

Last change on this file was 14137, checked in by rschlatte, 12 years ago

Update instance layout in (set-)standard-instance-access if necessary.

  • Reported by Pascal Costanza
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 20.3 KB
Line 
1/*
2 * StandardObject.java
3 *
4 * Copyright (C) 2003-2006 Peter Graves
5 * $Id: StandardObject.java 14137 2012-08-26 19:23:15Z rschlatte $
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
34package org.armedbear.lisp;
35
36import static org.armedbear.lisp.Lisp.*;
37
38public class StandardObject extends LispObject
39{
40  protected Layout layout;
41  protected LispObject[] slots;
42
43  protected StandardObject()
44  {
45    layout = new Layout(StandardClass.STANDARD_OBJECT, NIL, NIL);
46  }
47
48
49  protected StandardObject(Layout layout)
50  {
51    this(layout, layout.getLength());
52  }
53
54  protected StandardObject(Layout layout, int length)
55  {
56    this.layout = layout;
57    slots = new LispObject[length];
58    for (int i = slots.length; i-- > 0;)
59      slots[i] = UNBOUND_VALUE;
60  }
61
62
63  protected StandardObject(LispClass cls, int length)
64  {
65    layout = cls == null ? null : cls.getClassLayout();
66    slots = new LispObject[length];
67    for (int i = slots.length; i-- > 0;)
68      slots[i] = UNBOUND_VALUE;
69  }
70
71  protected StandardObject(LispClass cls)
72  {
73    layout = cls == null ? null : cls.getClassLayout();
74    slots = new LispObject[layout == null ? 0 : layout.getLength()];
75    for (int i = slots.length; i-- > 0;)
76      slots[i] = UNBOUND_VALUE;
77  }
78
79  @Override
80  public LispObject getParts()
81  {
82    LispObject parts = NIL;
83    if (layout != null)
84      {
85        if (layout.isInvalid())
86          {
87            // Update instance.
88            layout = updateLayout();
89          }
90      }
91    parts = parts.push(new Cons("LAYOUT", layout));
92    if (layout != null)
93      {
94        LispObject[] slotNames = layout.getSlotNames();
95        if (slotNames != null)
96          {
97            for (int i = 0; i < slotNames.length; i++)
98              {
99                parts = parts.push(new Cons(slotNames[i], slots[i]));
100              }
101          }
102      }
103    return parts.nreverse();
104  }
105
106  public final LispObject getLispClass()
107  {
108    return layout.getLispClass();
109  }
110
111  private LispObject helperGetClassName()
112  {
113    final LispObject c1 = layout.getLispClass();
114    if (c1 instanceof LispClass)
115        return ((LispClass)c1).getName();
116    else
117        return LispThread.currentThread().execute(Symbol.CLASS_NAME, c1);
118  }
119
120  private LispObject helperGetCPL()
121  {
122    final LispObject c1 = layout.getLispClass();
123    if (c1 instanceof LispClass)
124        return ((LispClass)c1).getCPL();
125    else
126        return LispThread.currentThread().execute(Symbol.CLASS_PRECEDENCE_LIST, c1);
127  }
128
129  @Override
130  public LispObject typeOf()
131  {
132    // "For objects of metaclass STRUCTURE-CLASS or STANDARD-CLASS, and for
133    // conditions, TYPE-OF returns the proper name of the class returned by
134    // CLASS-OF if it has a proper name, and otherwise returns the class
135    // itself."
136    final LispObject c1 = layout.getLispClass();
137    LispObject name;
138    if (c1 instanceof LispClass)
139        name = ((LispClass)c1).getName();
140    else
141        name = LispThread.currentThread().execute(Symbol.CLASS_NAME, c1);
142
143    // The proper name of a class is "a symbol that names the class whose
144    // name is that symbol".
145    if (name != NIL && name != UNBOUND_VALUE)
146      {
147        // TYPE-OF.9
148        final LispObject c2 = LispClass.findClass(name, false);
149        if (c2 == c1)
150          return name;
151      }
152    return c1;
153  }
154
155  @Override
156  public LispObject classOf()
157  {
158    return layout.getLispClass();
159  }
160
161  @Override
162  public LispObject typep(LispObject type)
163  {
164    if (type == Symbol.STANDARD_OBJECT)
165      return T;
166    if (type == StandardClass.STANDARD_OBJECT)
167      return T;
168    LispObject cls = layout != null ? layout.getLispClass() : null;
169    if (cls != null)
170      {
171        if (type == cls)
172          return T;
173        if (type == helperGetClassName())
174          return T;
175        LispObject cpl = helperGetCPL();
176        while (cpl != NIL)
177          {
178            if (type == cpl.car())
179              return T;
180
181            LispObject otherName;
182            LispObject otherClass = cpl.car();
183            if (otherClass instanceof LispClass) {
184              if (type == ((LispClass)otherClass).getName())
185                return T;
186            }
187            else
188            if (type == LispThread
189                .currentThread().execute(Symbol.CLASS_NAME, otherClass))
190                return T;
191
192            cpl = cpl.cdr();
193          }
194      }
195    return super.typep(type);
196  }
197
198  @Override
199  public String printObject()
200  {
201    final LispThread thread = LispThread.currentThread();
202    int maxLevel = Integer.MAX_VALUE;
203    LispObject printLevel = Symbol.PRINT_LEVEL.symbolValue(thread);
204    if (printLevel instanceof Fixnum)
205      maxLevel = ((Fixnum)printLevel).value;
206    LispObject currentPrintLevel =
207      _CURRENT_PRINT_LEVEL_.symbolValue(thread);
208    int currentLevel = Fixnum.getValue(currentPrintLevel);
209    if (currentLevel >= maxLevel)
210      return "#";
211    return unreadableString(typeOf().printObject());
212  }
213
214  Layout updateLayout()
215  {
216    Debug.assertTrue(layout.isInvalid());
217    Layout oldLayout = layout;
218    LispObject cls = oldLayout.getLispClass();
219    Layout newLayout;
220
221    if (cls instanceof LispClass)
222        newLayout = ((LispClass)cls).getClassLayout();
223    else
224        newLayout = (Layout)Symbol.CLASS_LAYOUT.execute(cls);
225
226    Debug.assertTrue(!newLayout.isInvalid());
227    StandardObject newInstance = new StandardObject(newLayout);
228    Debug.assertTrue(newInstance.layout == newLayout);
229    LispObject added = NIL;
230    LispObject discarded = NIL;
231    LispObject plist = NIL;
232    // Old local slots.
233    LispObject[] oldSlotNames = oldLayout.getSlotNames();
234    for (int i = 0; i < oldSlotNames.length; i++)
235      {
236        LispObject slotName = oldSlotNames[i];
237        int j = newLayout.getSlotIndex(slotName);
238        if (j >= 0)
239          newInstance.slots[j] = slots[i];
240        else
241          {
242            discarded = discarded.push(slotName);
243            if (slots[i] != UNBOUND_VALUE)
244              {
245                plist = plist.push(slotName);
246                plist = plist.push(slots[i]);
247              }
248          }
249      }
250    // Old shared slots.
251    LispObject rest = oldLayout.getSharedSlots(); // A list.
252    if (rest != null)
253      {
254        while (rest != NIL)
255          {
256            LispObject location = rest.car();
257            LispObject slotName = location.car();
258            int i = newLayout.getSlotIndex(slotName);
259            if (i >= 0)
260              newInstance.slots[i] = location.cdr();
261            rest = rest.cdr();
262          }
263      }
264    // Go through all the new local slots to compute the added slots.
265    LispObject[] newSlotNames = newLayout.getSlotNames();
266    for (int i = 0; i < newSlotNames.length; i++)
267      {
268        LispObject slotName = newSlotNames[i];
269        int j = oldLayout.getSlotIndex(slotName);
270        if (j >= 0)
271          continue;
272        LispObject location = oldLayout.getSharedSlotLocation(slotName);
273        if (location != null)
274          continue;
275        // Not found.
276        added = added.push(slotName);
277      }
278    // Swap slots.
279    LispObject[] tempSlots = slots;
280    slots = newInstance.slots;
281    newInstance.slots = tempSlots;
282    // Swap layouts.
283    Layout tempLayout = layout;
284    layout = newInstance.layout;
285    newInstance.layout = tempLayout;
286    Debug.assertTrue(!layout.isInvalid());
287    // Call UPDATE-INSTANCE-FOR-REDEFINED-CLASS.
288    Symbol.UPDATE_INSTANCE_FOR_REDEFINED_CLASS.execute(this, added,
289                                                       discarded, plist);
290    return newLayout;
291  }
292
293  // Only handles instance slots (not shared slots).
294  public LispObject getInstanceSlotValue(LispObject slotName)
295
296  {
297    Debug.assertTrue(layout != null);
298    if (layout.isInvalid())
299      {
300        // Update instance.
301        layout = updateLayout();
302      }
303    Debug.assertTrue(layout != null);
304    int index = layout.getSlotIndex(slotName);
305    //### FIXME: should call SLOT-MISSING (clhs)
306    if (index < 0)
307      return error(new LispError("Missing slot " + slotName.princToString()));
308    return slots[index];
309  }
310
311  // Only handles instance slots (not shared slots).
312  public void setInstanceSlotValue(LispObject slotName, LispObject newValue)
313
314  {
315    Debug.assertTrue(layout != null);
316    if (layout.isInvalid())
317      {
318        // Update instance.
319        layout = updateLayout();
320      }
321    Debug.assertTrue(layout != null);
322    int index = layout.getSlotIndex(slotName);
323    // FIXME: should call SLOT-MISSING (clhs)
324    if (index < 0)
325      error(new LispError("Missing slot " + slotName.princToString()));
326    slots[index] = newValue;
327  }
328 
329  final public static StandardObject checkStandardObject(LispObject first)
330  {
331    if (first instanceof StandardObject)
332      return (StandardObject) first;
333    return (StandardObject) type_error(first, Symbol.STANDARD_OBJECT);
334  }
335       
336  private static final Primitive SWAP_SLOTS
337    = new pf_swap_slots(); 
338  @DocString(name="swap-slots",
339             args="instance-1 instance-2",
340             returns="nil")
341  private static final class pf_swap_slots extends Primitive
342  {
343    pf_swap_slots()
344    {
345      super("swap-slots", PACKAGE_SYS, true, "instance-1 instance-2");
346    }
347    @Override
348    public LispObject execute(LispObject first, LispObject second)
349    {
350      final StandardObject obj1 = checkStandardObject(first);
351      final StandardObject obj2 = checkStandardObject(second);
352      LispObject[] temp = obj1.slots;
353      obj1.slots = obj2.slots;
354      obj2.slots = temp;
355      return NIL;
356    }
357  };
358
359  private static final Primitive STD_INSTANCE_LAYOUT
360    = new pf_std_instance_layout();
361  @DocString(name="std-instance-layout")
362  private static final class pf_std_instance_layout extends Primitive
363  {
364    pf_std_instance_layout() 
365    {
366      super("std-instance-layout", PACKAGE_SYS, true);
367    }
368    @Override
369    public LispObject execute(LispObject arg)
370    {
371      final StandardObject instance = checkStandardObject(arg);
372      Layout layout = instance.layout;
373      if (layout.isInvalid())
374        {
375          // Update instance.
376          layout = instance.updateLayout();
377        }
378      return layout;
379    }
380  };
381
382  private static final Primitive _SET_STD_INSTANCE_LAYOUT
383    = new pf__set_std_instance_layout();
384  @DocString(name="%set-std-instance-layout")
385  private static final class pf__set_std_instance_layout extends Primitive
386  {
387    pf__set_std_instance_layout()
388    {
389      super("%set-std-instance-layout", PACKAGE_SYS, true);
390    }
391    @Override
392    public LispObject execute(LispObject first, LispObject second)
393    {
394      checkStandardObject(first).layout = checkLayout(second);         
395      return second;
396    }
397  };
398
399  private static final Primitive STD_INSTANCE_CLASS
400    = new pf_std_instance_class(); 
401  @DocString(name="std-instance-class")
402  private static final class pf_std_instance_class extends Primitive
403  {
404    pf_std_instance_class()
405    {
406      super("std-instance-class", PACKAGE_SYS, true);
407    }
408    @Override
409    public LispObject execute(LispObject arg)
410    {
411      return checkStandardObject(arg).layout.getLispClass();
412    }
413  };
414
415  private static final Primitive STANDARD_INSTANCE_ACCESS
416    = new pf_standard_instance_access();
417  @DocString(name="standard-instance-access",
418             args="instance location",
419             returns="value")
420  private static final class pf_standard_instance_access extends Primitive
421  {
422    pf_standard_instance_access()
423    {
424      super("standard-instance-access", PACKAGE_SYS, true,
425            "instance location");
426    }
427    @Override
428    public LispObject execute(LispObject first, LispObject second)
429    {
430      final StandardObject instance = checkStandardObject(first);
431      if (instance.layout.isInvalid()) {
432        // Update instance.
433        instance.updateLayout();
434      }
435      final int index;
436      if (second instanceof Fixnum) {
437        index = ((Fixnum)second).value;
438      } else {
439        return type_error(second, Symbol.INTEGER);
440      }
441
442      LispObject value;
443      try {
444        value = instance.slots[index];
445      } catch (ArrayIndexOutOfBoundsException e) {
446        if (instance.slots.length > 0)
447          return type_error(second,
448                            list(Symbol.INTEGER, Fixnum.ZERO,
449                                 Fixnum.getInstance(instance.slots.length - 1)));
450        else
451          return error(new ProgramError("The object "
452                                        + instance.princToString() +
453                                        " has no slots."));
454
455      }
456      // We let UNBOUND_VALUE escape here, since invoking
457      // standard-instance-access on an unbound slot has undefined
458      // consequences (AMOP pg. 239), and we use this behavior to
459      // implement slot-boundp-using-class.
460      return value;
461    }
462  };
463
464  private static final Primitive _SET_STANDARD_INSTANCE_ACCESS
465    = new pf__set_standard_instance_access();
466  @DocString(name="%set-standard-instance-access",
467             args="instance location new-value",
468             returns="new-value")
469  private static final class pf__set_standard_instance_access extends Primitive
470  {
471    pf__set_standard_instance_access()
472    {
473      super("%set-standard-instance-access", PACKAGE_SYS, true);
474    }
475    @Override
476    public LispObject execute(LispObject first, LispObject second,
477                              LispObject third)
478    {
479      final StandardObject instance = checkStandardObject(first);
480      if (instance.layout.isInvalid()) {
481        // Update instance.
482        instance.updateLayout();
483      }
484      final int index;
485      if (second instanceof Fixnum) {
486        index = ((Fixnum)second).value;
487      } else {
488        return type_error(second, Symbol.INTEGER);
489      }
490      try {
491        instance.slots[index] = third;
492      } catch (ArrayIndexOutOfBoundsException e) {
493        if (instance.slots.length > 0)
494          return type_error(second,
495                            list(Symbol.INTEGER, Fixnum.ZERO,
496                                 Fixnum.getInstance(instance.slots.length - 1)));
497        else
498          return error(new ProgramError("The object "
499                                        + instance.princToString() +
500                                        " has no slots."));
501
502      }
503      return third;
504    }
505  };
506
507  private static final Primitive STD_SLOT_BOUNDP
508    = new pf_std_slot_boundp();
509  @DocString(name="std-slot-boundp")
510  private static final class pf_std_slot_boundp extends Primitive
511  {
512    pf_std_slot_boundp()
513    {
514      super(Symbol.STD_SLOT_BOUNDP, "instance slot-name");
515    }
516    @Override
517    public LispObject execute(LispObject first, LispObject second)
518    {
519      final StandardObject instance = checkStandardObject(first);
520      Layout layout = instance.layout;
521      if (layout.isInvalid())
522        {
523          // Update instance.
524          layout = instance.updateLayout();
525        }
526      final LispObject index = layout.slotTable.get(second);
527      if (index != null)
528        {
529          // Found instance slot.
530          return instance.slots[((Fixnum)index).value] != UNBOUND_VALUE ? T : NIL;
531        }
532      // Check for shared slot.
533      final LispObject location = layout.getSharedSlotLocation(second);
534      if (location != null)
535        return location.cdr() != UNBOUND_VALUE ? T : NIL;
536      // Not found.
537      final LispThread thread = LispThread.currentThread();
538      LispObject value =
539        thread.execute(Symbol.SLOT_MISSING, instance.getLispClass(),
540                       instance, second, Symbol.SLOT_BOUNDP);
541      // "If SLOT-MISSING is invoked and returns a value, a boolean
542      // equivalent to its primary value is returned by SLOT-BOUNDP."
543      thread._values = null;
544      return value != NIL ? T : NIL;
545    }
546  };
547
548  @Override
549  public LispObject SLOT_VALUE(LispObject slotName)
550  {
551    if (layout.isInvalid())
552      {
553        // Update instance.
554        layout = updateLayout();
555      }
556    LispObject value;
557    final LispObject index = layout.slotTable.get(slotName);
558    if (index != null)
559      {
560        // Found instance slot.
561        value = slots[((Fixnum)index).value];
562      }
563    else
564      {
565        // Check for shared slot.
566        LispObject location = layout.getSharedSlotLocation(slotName);
567        if (location == null)
568          return Symbol.SLOT_MISSING.execute(getLispClass(), this, slotName,
569                                             Symbol.SLOT_VALUE);
570        value = location.cdr();
571      }
572    if (value == UNBOUND_VALUE)
573      {
574        value = Symbol.SLOT_UNBOUND.execute(getLispClass(), this, slotName);
575        LispThread.currentThread()._values = null;
576      }
577    return value;
578  }
579
580  private static final Primitive STD_SLOT_VALUE
581    = new pf_std_slot_value();
582  @DocString(name="std-slot-value")
583  private static final class pf_std_slot_value extends Primitive
584  {
585    pf_std_slot_value()
586    {
587      super(Symbol.STD_SLOT_VALUE, "instance slot-name");
588    }
589    @Override
590    public LispObject execute(LispObject first, LispObject second)
591    {
592      return first.SLOT_VALUE(second);
593    }
594  };
595
596  @Override
597  public void setSlotValue(LispObject slotName, LispObject newValue)
598  {
599    if (layout.isInvalid())
600      {
601        // Update instance.
602        layout = updateLayout();
603      }
604    final LispObject index = layout.slotTable.get(slotName);
605    if (index != null)
606      {
607        // Found instance slot.
608        slots[((Fixnum)index).value] = newValue;
609        return;
610      }
611    // Check for shared slot.
612    LispObject location = layout.getSharedSlotLocation(slotName);
613    if (location != null)
614      {
615        location.setCdr(newValue);
616        return;
617      }
618    LispObject[] args = new LispObject[5];
619    args[0] = getLispClass();
620    args[1] = this;
621    args[2] = slotName;
622    args[3] = Symbol.SETF;
623    args[4] = newValue;
624    Symbol.SLOT_MISSING.execute(args);
625  }
626
627  private static final Primitive SET_STD_SLOT_VALUE
628    = new pf_set_std_slot_value();
629  @DocString(name="set-std-slot-value")
630  private static final class pf_set_std_slot_value extends Primitive
631  {
632    pf_set_std_slot_value()
633    {
634      super(Symbol.SET_STD_SLOT_VALUE, "instance slot-name new-value");
635    }
636    @Override
637    public LispObject execute(LispObject first, LispObject second,
638                              LispObject third)
639    {
640      first.setSlotValue(second, third);
641      return third;
642    }
643  };
644
645  private static final Primitive _STD_ALLOCATE_INSTANCE
646    = new pf__std_allocate_instance();
647  @DocString(name="%std-allocate-instance",
648             args="class",
649             returns="instance")
650  private static final class pf__std_allocate_instance extends Primitive
651  {
652    pf__std_allocate_instance()
653    {
654      super("%std-allocate-instance", PACKAGE_SYS, true, "class");
655    }
656    @Override
657    public LispObject execute(LispObject arg)
658    {
659      if (arg == StandardClass.STANDARD_CLASS)
660        return new StandardClass();
661      if (arg instanceof StandardClass)
662        return ((StandardClass)arg).allocateInstance();
663      if (arg.typep(StandardClass.STANDARD_CLASS) != NIL) {
664        LispObject l = Symbol.CLASS_LAYOUT.execute(arg);
665        if (! (l instanceof Layout))
666          return error(new ProgramError("Invalid standard class layout for: " + arg.princToString()));
667       
668        return new StandardObject((Layout)l);
669      }
670      return type_error(arg, Symbol.STANDARD_CLASS);
671    }
672  };
673}
Note: See TracBrowser for help on using the repository browser.