Skip to content

Commit

Permalink
Fix CLJ-801: protocol's method cache falls back to using a map when s…
Browse files Browse the repository at this point in the history
…hift-mask table won't work, instead of throwing an exception.

Signed-off-by: Stuart Sierra <mail@stuartsierra.com>
  • Loading branch information
Alexander Taggart authored and Stuart Sierra committed Jun 21, 2011
1 parent 66a88de commit 8b94a54
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 33 deletions.
9 changes: 0 additions & 9 deletions src/clj/clojure/core.clj
Expand Up @@ -5795,15 +5795,6 @@
shift (range 0 31)]
[shift mask]))))

(defn- min-hash
"takes a collection of keys and returns [shift mask]"
[keys]
(let [hashes (map hash keys)
_ (when-not (apply distinct? hashes)
(throw (IllegalArgumentException. "Hashes must be distinct")))
sm (maybe-min-hash hashes)]
(or sm (throw (IllegalArgumentException. "No distinct mapping found")))))

(defn- case-map
"Transforms a sequence of test constants and a corresponding sequence of then
expressions into a sorted map to be consumed by case*. The form of the map
Expand Down
26 changes: 15 additions & 11 deletions src/clj/clojure/core_deftype.clj
Expand Up @@ -417,17 +417,21 @@
;;;;;;;;;;;;;;;;;;;;;;; protocols ;;;;;;;;;;;;;;;;;;;;;;;;

(defn- expand-method-impl-cache [^clojure.lang.MethodImplCache cache c f]
(let [cs (into1 {} (remove (fn [[c e]] (nil? e)) (map vec (partition 2 (.table cache)))))
cs (assoc cs c (clojure.lang.MethodImplCache$Entry. c f))
[shift mask] (min-hash (keys cs))
table (make-array Object (* 2 (inc mask)))
table (reduce1 (fn [^objects t [c e]]
(let [i (* 2 (int (shift-mask shift mask (hash c))))]
(aset t i c)
(aset t (inc i) e)
t))
table cs)]
(clojure.lang.MethodImplCache. (.protocol cache) (.methodk cache) shift mask table)))
(if (.map cache)
(let [cs (assoc (.map cache) c (clojure.lang.MethodImplCache$Entry. c f))]
(clojure.lang.MethodImplCache. (.protocol cache) (.methodk cache) cs))
(let [cs (into1 {} (remove (fn [[c e]] (nil? e)) (map vec (partition 2 (.table cache)))))
cs (assoc cs c (clojure.lang.MethodImplCache$Entry. c f))]
(if-let [[shift mask] (maybe-min-hash (map hash (keys cs)))]
(let [table (make-array Object (* 2 (inc mask)))
table (reduce1 (fn [^objects t [c e]]
(let [i (* 2 (int (shift-mask shift mask (hash c))))]
(aset t i c)
(aset t (inc i) e)
t))
table cs)]
(clojure.lang.MethodImplCache. (.protocol cache) (.methodk cache) shift mask table))
(clojure.lang.MethodImplCache. (.protocol cache) (.methodk cache) cs)))))

(defn- super-chain [^Class c]
(when c
Expand Down
48 changes: 35 additions & 13 deletions src/jvm/clojure/lang/MethodImplCache.java
Expand Up @@ -12,6 +12,8 @@

package clojure.lang;

import java.util.Map;

public final class MethodImplCache{

static public class Entry{
Expand All @@ -29,6 +31,7 @@ public Entry(Class c, IFn fn){
public final int shift;
public final int mask;
public final Object[] table; //[class, entry. class, entry ...]
public final Map map;

Entry mre = null;

Expand All @@ -37,11 +40,21 @@ public MethodImplCache(IPersistentMap protocol, Keyword methodk){
}

public MethodImplCache(IPersistentMap protocol, Keyword methodk, int shift, int mask, Object[] table){
this.protocol = protocol;
this.methodk = methodk;
this.shift = shift;
this.mask = mask;
this.table = table;
this.protocol = protocol;
this.methodk = methodk;
this.shift = shift;
this.mask = mask;
this.table = table;
this.map = null;
}

public MethodImplCache(IPersistentMap protocol, Keyword methodk, Map map){
this.protocol = protocol;
this.methodk = methodk;
this.shift = 0;
this.mask = 0;
this.table = null;
this.map = map;
}

public IFn fnFor(Class c){
Expand All @@ -52,14 +65,23 @@ public IFn fnFor(Class c){
}

IFn findFnFor(Class c){
int idx = ((Util.hash(c) >> shift) & mask) << 1;
if(idx < table.length && table[idx] == c)
{
Entry e = ((Entry) table[idx + 1]);
mre = e;
return e != null ? e.fn : null;
}
return null;
if (map != null)
{
Entry e = (Entry) map.get(c);
mre = e;
return e != null ? e.fn : null;
}
else
{
int idx = ((Util.hash(c) >> shift) & mask) << 1;
if(idx < table.length && table[idx] == c)
{
Entry e = ((Entry) table[idx + 1]);
mre = e;
return e != null ? e.fn : null;
}
return null;
}
}


Expand Down

0 comments on commit 8b94a54

Please sign in to comment.