source: trunk/abcl/src/org/armedbear/lisp/EMFCache.java

Last change on this file was 14495, checked in by rschlatte, 11 years ago

Remove StandardGenericFunction? class

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 9.0 KB
Line 
1/*
2 * EMFCache.java
3 *
4 * Copyright (C) 2003-2006 Peter Graves, 2013 Rudolf Schlatte
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 *
20 * As a special exception, the copyright holders of this library give you
21 * permission to link this library with independent modules to produce an
22 * executable, regardless of the license terms of these independent
23 * modules, and to copy and distribute the resulting executable under
24 * terms of your choice, provided that you also meet, for each linked
25 * independent module, the terms and conditions of the license of that
26 * module.  An independent module is a module which is not derived from
27 * or based on this library.  If you modify this library, you may extend
28 * this exception to your version of the library, but you are not
29 * obligated to do so.  If you do not wish to do so, delete this
30 * exception statement from your version.
31 */
32
33package org.armedbear.lisp;
34
35import static org.armedbear.lisp.Lisp.*;
36
37import java.util.concurrent.ConcurrentHashMap;
38
39public final class EMFCache extends LispObject
40{
41  ConcurrentHashMap<CacheEntry,LispObject> cache
42    = new ConcurrentHashMap<CacheEntry,LispObject>();;
43  EqlSpecialization eqlSpecializations[] = new EqlSpecialization[0];
44
45  void clearCache()
46  {
47    cache = new ConcurrentHashMap<CacheEntry,LispObject>();
48  }
49
50  @Override
51  public String printObject()
52  {
53    return unreadableString("EMF-CACHE");
54  }
55
56  static final FuncallableStandardObject checkStandardGenericFunction(LispObject obj)
57  {
58    if (obj instanceof FuncallableStandardObject)
59      return (FuncallableStandardObject) obj;
60    return (FuncallableStandardObject) // Not reached.
61      type_error(obj, Symbol.STANDARD_GENERIC_FUNCTION);
62  }
63
64  private static class EqlSpecialization extends LispObject
65  {
66    public LispObject eqlTo;
67
68    public EqlSpecialization(LispObject eqlTo)
69    {
70        this.eqlTo = eqlTo;
71    }
72  }
73
74  private static class CacheEntry
75  {
76    final LispObject[] array;
77
78    CacheEntry(LispObject[] array)
79    {
80      this.array = array;
81    }
82
83    @Override
84    public int hashCode()
85    {
86      int result = 0;
87      for (int i = array.length; i-- > 0;)
88        result ^= array[i].hashCode();
89      return result;
90    }
91
92    @Override
93    public boolean equals(Object object)
94    {
95      if (!(object instanceof CacheEntry))
96        return false;
97      final CacheEntry otherEntry = (CacheEntry) object;
98      if (otherEntry.array.length != array.length)
99        return false;
100      final LispObject[] otherArray = otherEntry.array;
101      for (int i = array.length; i-- > 0;)
102        if (array[i] != otherArray[i])
103          return false;
104      return true;
105    }
106  }
107
108  private static final Primitive _MAKE_EMF_CACHE
109    = new pf__make_emf_cache();
110  @DocString(name="%make-emf-cache")
111  private static final class  pf__make_emf_cache extends Primitive
112  {
113    pf__make_emf_cache()
114    {
115      super("%make-emf-cache", PACKAGE_SYS, true);
116    }
117    @Override
118    public LispObject execute(LispObject arg)
119    {
120      return new EMFCache();
121    }
122  };
123
124  private static final Primitive _REINIT_EMF_CACHE
125    = new pf__reinit_emf_cache();
126  @DocString(name="%reinit-emf-cache",
127             args="generic-function eql-specilizer-objects-list")
128  private static final class  pf__reinit_emf_cache extends Primitive
129  {
130    pf__reinit_emf_cache()
131    {
132      super("%reinit-emf-cache", PACKAGE_SYS, true,
133            "generic-function eql-specializer-objects-list");
134    }
135    @Override
136    public LispObject execute(LispObject generic_function, LispObject eql_specializers)
137    {
138      final FuncallableStandardObject gf = checkStandardGenericFunction(generic_function);
139      EMFCache cache = gf.cache;
140      cache.clearCache();
141      cache.eqlSpecializations = new EqlSpecialization[eql_specializers.length()];
142      for (int i = 0; i < cache.eqlSpecializations.length; i++) {
143        cache.eqlSpecializations[i] = new EqlSpecialization(eql_specializers.car());
144        eql_specializers = eql_specializers.cdr();
145      }
146      return T;
147    }
148  };
149
150  private static final Primitive CACHE_EMF
151    = new pf_cache_emf();
152  @DocString(name="cache-emf",
153             args="generic-function args emf")
154  private static final class pf_cache_emf extends Primitive
155  {
156    pf_cache_emf()
157    {
158      super("cache-emf", PACKAGE_SYS, true, "generic-function args emf");
159    }
160    @Override
161    public LispObject execute(LispObject first, LispObject second,
162                              LispObject third)
163    {
164      final FuncallableStandardObject gf = checkStandardGenericFunction(first);
165      EMFCache cache = gf.cache;
166      LispObject args = second;
167      int numberOfRequiredArgs
168        = gf.getInstanceSlotValue(Symbol.REQUIRED_ARGS).length();
169      LispObject[] array = new LispObject[numberOfRequiredArgs];
170      for (int i = numberOfRequiredArgs; i-- > 0;)
171        {
172          array[i] = cache.getArgSpecialization(args.car());
173          args = args.cdr();
174        }
175      CacheEntry specializations = new CacheEntry(array);
176      ConcurrentHashMap<CacheEntry,LispObject> ht = cache.cache;
177      ht.put(specializations, third);
178      return third;
179    }
180  };
181
182  private static final Primitive GET_CACHED_EMF
183    = new pf_get_cached_emf();
184  @DocString(name="get-cached-emf",
185             args="generic-function args")
186  private static final class pf_get_cached_emf extends Primitive
187  {
188    pf_get_cached_emf() {
189      super("get-cached-emf", PACKAGE_SYS, true, "generic-function args");
190    }
191    @Override
192    public LispObject execute(LispObject first, LispObject second)
193    {
194      final FuncallableStandardObject gf = checkStandardGenericFunction(first);
195      EMFCache cache = gf.cache;
196      LispObject args = second;
197      int numberOfRequiredArgs
198        = gf.getInstanceSlotValue(Symbol.REQUIRED_ARGS).length();
199      LispObject[] array = new LispObject[numberOfRequiredArgs];
200      for (int i = numberOfRequiredArgs; i-- > 0;)
201        {
202          array[i] = cache.getArgSpecialization(args.car());
203          args = args.cdr();
204        }
205      CacheEntry specializations = new CacheEntry(array);
206      ConcurrentHashMap<CacheEntry,LispObject> ht = cache.cache;
207      LispObject emf = (LispObject) ht.get(specializations);
208      return emf != null ? emf : NIL;
209    }
210  };
211
212  /**
213   * Returns an object representing generic function
214   * argument <tt>arg</tt> in a <tt>CacheEntry</tt>
215   *
216   * <p>In the simplest case, when this generic function
217   * does not have EQL specialized methods, and therefore
218   * only argument types are relevant for choosing
219   * applicable methods, the value returned is the
220   * class of <tt>arg</tt>
221   *
222   * <p>If the function has EQL specialized methods:
223   *   - if <tt>arg</tt> is EQL to some of the EQL-specializers,
224   *     a special object representing equality to that specializer
225   *     is returned.
226   *   - otherwise class of the <tt>arg</tt> is returned.
227   *
228   * <p>Note that we do not consider argument position, when
229   * calculating arg specialization. In rare cases (when one argument
230   * is eql-specialized to a symbol specifying class of another
231   * argument) this may result in redundant cache entries caching the
232   * same method. But the method cached is anyway correct for the
233   * arguments (because in case of cache miss, correct method is
234   * calculated by other code, which does not rely on
235   * getArgSpecialization; and because EQL is true only for objects of
236   * the same type, which guaranties that if a type-specialized
237   * methods was chached by eql-specialization, all the cache hits
238   * into this records will be from args of the conforming type).
239   *
240   * <p>Consider:
241   * <pre><tt>
242   * (defgeneric f (a b))
243   *
244   * (defmethod f (a (b (eql 'symbol)))
245   *   "T (EQL 'SYMBOL)")
246   *
247   * (defmethod f ((a symbol) (b (eql 'symbol)))
248   *   "SYMBOL (EQL 'SYMBOL)")
249   *
250   * (f 12 'symbol)
251   * => "T (EQL 'SYMBOL)"
252   *
253   * (f 'twelve 'symbol)
254   * => "SYMBOL (EQL 'SYMBOL)"
255   *
256   * (f 'symbol 'symbol)
257   * => "SYMBOL (EQL 'SYMBOL)"
258   *
259   * </tt></pre>
260   *
261   * After the two above calls <tt>cache</tt> will contain three keys:
262   * <pre>
263   * { class FIXNUM, EqlSpecialization('SYMBOL) }
264   * { class SYMBOL, EqlSpecialization('SYMBOL) }
265   * { EqlSpecialization('SYMBOL), EqlSpecialization('SYMBOL) }.
266   * </pre>
267   */
268  LispObject getArgSpecialization(LispObject arg)
269  {
270    for (EqlSpecialization eqlSpecialization : eqlSpecializations)
271      {
272        if (eqlSpecialization.eqlTo.eql(arg))
273          return eqlSpecialization;
274      }
275    return arg.classOf();
276  }
277
278}
Note: See TracBrowser for help on using the repository browser.