ConstantData.java revision 1350:3cb11f4d617e
1/*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.nashorn.internal.codegen;
27
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.HashMap;
31import java.util.List;
32import java.util.Map;
33import java.util.Objects;
34
35import jdk.nashorn.internal.runtime.PropertyMap;
36
37/**
38 * Manages constants needed by code generation.  Objects are maintained in an
39 * interning maps to remove duplicates.
40 */
41final class ConstantData {
42    /** Constant table. */
43    final List<Object> constants;
44
45    /** Constant table string interning map. */
46    final Map<String, Integer> stringMap;
47
48    /** Constant table object interning map. */
49    final Map<Object, Integer> objectMap;
50
51    private static class ArrayWrapper {
52        private final Object array;
53        private final int    hashCode;
54
55        public ArrayWrapper(final Object array) {
56            this.array    = array;
57            this.hashCode = calcHashCode();
58        }
59
60        /**
61         * Calculate a shallow hashcode for the array.
62         * @return Hashcode with elements factored in.
63         */
64        private int calcHashCode() {
65            final Class<?> cls = array.getClass();
66
67            if (!cls.getComponentType().isPrimitive()) {
68                return Arrays.hashCode((Object[])array);
69            } else if (cls == double[].class) {
70                return Arrays.hashCode((double[])array);
71            } if (cls == long[].class) {
72                return Arrays.hashCode((long[])array);
73            } if (cls == int[].class) {
74                return Arrays.hashCode((int[])array);
75            }
76
77            throw new AssertionError("ConstantData doesn't support " + cls);
78        }
79
80        @Override
81        public boolean equals(final Object other) {
82            if (!(other instanceof ArrayWrapper)) {
83                return false;
84            }
85
86            final Object otherArray = ((ArrayWrapper)other).array;
87
88            if (array == otherArray) {
89                return true;
90            }
91
92            final Class<?> cls = array.getClass();
93
94            if (cls == otherArray.getClass()) {
95                if (!cls.getComponentType().isPrimitive()) {
96                    return Arrays.equals((Object[])array, (Object[])otherArray);
97                } else if (cls == double[].class) {
98                    return Arrays.equals((double[])array, (double[])otherArray);
99                } else if (cls == long[].class) {
100                    return Arrays.equals((long[])array, (long[])otherArray);
101                } else if (cls == int[].class) {
102                    return Arrays.equals((int[])array, (int[])otherArray);
103                }
104            }
105
106            return false;
107        }
108
109        @Override
110        public int hashCode() {
111            return hashCode;
112        }
113    }
114
115    /**
116     * {@link PropertyMap} wrapper class that provides implementations for the {@code hashCode} and {@code equals}
117     * methods that are based on the map layout. {@code PropertyMap} itself inherits the identity based implementations
118     * from {@code java.lang.Object}.
119     */
120    private static class PropertyMapWrapper {
121        private final PropertyMap propertyMap;
122        private final int hashCode;
123
124        public PropertyMapWrapper(final PropertyMap map) {
125            this.hashCode = Arrays.hashCode(map.getProperties()) + 31 * Objects.hashCode(map.getClassName());
126            this.propertyMap = map;
127        }
128
129        @Override
130        public int hashCode() {
131            return hashCode;
132        }
133
134        @Override
135        public boolean equals(final Object other) {
136            if (!(other instanceof PropertyMapWrapper)) {
137                return false;
138            }
139            final PropertyMap otherMap = ((PropertyMapWrapper) other).propertyMap;
140            return propertyMap == otherMap
141                    || (Arrays.equals(propertyMap.getProperties(), otherMap.getProperties())
142                        && Objects.equals(propertyMap.getClassName(), otherMap.getClassName()));
143        }
144    }
145
146    /**
147     * Constructor
148     */
149    ConstantData() {
150        this.constants = new ArrayList<>();
151        this.stringMap = new HashMap<>();
152        this.objectMap = new HashMap<>();
153    }
154
155    /**
156     * Add a string to the constant data
157     *
158     * @param string the string to add
159     * @return the index in the constant pool that the string was given
160     */
161    public int add(final String string) {
162        final Integer value = stringMap.get(string);
163
164        if (value != null) {
165            return value;
166        }
167
168        constants.add(string);
169        final int index = constants.size() - 1;
170        stringMap.put(string, index);
171
172        return index;
173    }
174
175    /**
176     * Add an object to the constant data
177     *
178     * @param object the string to add
179     * @return the index in the constant pool that the object was given
180     */
181    public int add(final Object object) {
182        assert object != null;
183        final Object  entry;
184        if (object.getClass().isArray()) {
185            entry = new ArrayWrapper(object);
186        } else if (object instanceof PropertyMap) {
187            entry = new PropertyMapWrapper((PropertyMap) object);
188        } else {
189            entry = object;
190        }
191        final Integer value = objectMap.get(entry);
192
193        if (value != null) {
194            return value;
195        }
196
197        constants.add(object);
198        final int index = constants.size() - 1;
199        objectMap.put(entry, index);
200
201        return index;
202    }
203
204    Object[] toArray() {
205        return constants.toArray();
206    }
207}
208