001/*
002 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.compiler.common;
024
025import java.util.*;
026import java.util.function.*;
027
028/**
029 * A map that combines {@link IdentityHashMap} with {@link LinkedHashMap} for the purpose of
030 * ensuring a deterministic execution order during a capturing compilation.
031 */
032final class LinkedIdentityHashMap<K, V> implements Map<K, V> {
033
034    private final LinkedHashMap<Id<K>, V> map;
035
036    public LinkedIdentityHashMap() {
037        map = new LinkedHashMap<>();
038    }
039
040    public LinkedIdentityHashMap(Map<K, V> m) {
041        map = new LinkedHashMap<>(m.size());
042        putAll(m);
043    }
044
045    public LinkedIdentityHashMap(int expectedMaxSize) {
046        map = new LinkedHashMap<>(expectedMaxSize);
047    }
048
049    /**
050     * Wrapper for an object that gives uses the object's identity for the purpose of equality
051     * comparisons and computing a hash code.
052     */
053    static final class Id<T> {
054        final T object;
055
056        public Id(T object) {
057            assert object != null;
058            this.object = object;
059        }
060
061        @SuppressWarnings("unchecked")
062        @Override
063        public boolean equals(Object obj) {
064            return obj instanceof Id && ((Id<T>) obj).object == object;
065        }
066
067        @Override
068        public int hashCode() {
069            return System.identityHashCode(object);
070        }
071    }
072
073    public int size() {
074        return map.size();
075    }
076
077    public boolean isEmpty() {
078        return map.isEmpty();
079    }
080
081    public boolean containsKey(Object key) {
082        return map.containsKey(id(key));
083    }
084
085    @SuppressWarnings("unchecked")
086    private Id<K> id(Object key) {
087        if (key == null) {
088            return null;
089        }
090        return new Id<>((K) key);
091    }
092
093    public boolean containsValue(Object value) {
094        return map.containsValue(value);
095    }
096
097    public V get(Object key) {
098        return map.get(id(key));
099    }
100
101    public V put(K key, V value) {
102        return map.put(id(key), value);
103    }
104
105    public V remove(Object key) {
106        return map.remove(id(key));
107    }
108
109    @SuppressWarnings("unchecked")
110    public void putAll(Map<? extends K, ? extends V> m) {
111        if (m == null) {
112            throw new NullPointerException();
113        }
114        if (m.getClass() == getClass()) {
115            LinkedIdentityHashMap<K, V> that = (LinkedIdentityHashMap<K, V>) m;
116            map.putAll(that.map);
117
118        } else {
119            for (K key : m.keySet()) {
120                map.put(id(key), m.get(key));
121            }
122        }
123    }
124
125    public void clear() {
126        map.clear();
127    }
128
129    final class KeySet extends AbstractSet<K> {
130        @Override
131        public int size() {
132            return map.size();
133        }
134
135        @Override
136        public void clear() {
137            map.clear();
138        }
139
140        @Override
141        public Iterator<K> iterator() {
142            return new Iterator<K>() {
143                final Iterator<Id<K>> i = map.keySet().iterator();
144
145                public boolean hasNext() {
146                    return i.hasNext();
147                }
148
149                public K next() {
150                    return i.next().object;
151                }
152
153                public void remove() {
154                    i.remove();
155                }
156            };
157        }
158
159        @Override
160        public boolean contains(Object o) {
161            return containsKey(o);
162        }
163
164        @Override
165        public boolean remove(Object o) {
166            return LinkedIdentityHashMap.this.remove(o) != null;
167        }
168
169        public Spliterator<K> spliterator() {
170            return Spliterators.spliterator(this, Spliterator.SIZED | Spliterator.ORDERED | Spliterator.DISTINCT);
171        }
172
173        public void forEach(Consumer<? super K> action) {
174            throw new UnsupportedOperationException();
175        }
176    }
177
178    public Set<K> keySet() {
179        return new KeySet();
180    }
181
182    public Collection<V> values() {
183        return map.values();
184    }
185
186    final class EntrySet extends AbstractSet<Map.Entry<K, V>> {
187        @Override
188        public int size() {
189            return map.size();
190        }
191
192        @Override
193        public void clear() {
194            map.clear();
195        }
196
197        @Override
198        public Iterator<Map.Entry<K, V>> iterator() {
199            return new Iterator<Map.Entry<K, V>>() {
200                final Iterator<Map.Entry<Id<K>, V>> i = map.entrySet().iterator();
201
202                public boolean hasNext() {
203                    return i.hasNext();
204                }
205
206                public Map.Entry<K, V> next() {
207                    Map.Entry<Id<K>, V> e = i.next();
208                    return new Map.Entry<K, V>() {
209
210                        public K getKey() {
211                            return e.getKey().object;
212                        }
213
214                        public V getValue() {
215                            return e.getValue();
216                        }
217
218                        public V setValue(V value) {
219                            return e.setValue(value);
220                        }
221                    };
222                }
223
224                public void remove() {
225                    i.remove();
226                }
227            };
228        }
229
230        @Override
231        public boolean contains(Object o) {
232            throw new UnsupportedOperationException();
233        }
234
235        @Override
236        public boolean remove(Object o) {
237            throw new UnsupportedOperationException();
238        }
239
240        public Spliterator<Map.Entry<K, V>> spliterator() {
241            throw new UnsupportedOperationException();
242        }
243
244        public void forEach(Consumer<? super Map.Entry<K, V>> action) {
245            throw new UnsupportedOperationException();
246        }
247    }
248
249    public Set<Map.Entry<K, V>> entrySet() {
250        return new EntrySet();
251    }
252}