blob: a7e6653945a785031f939b8228330bbee6765b0f [file] [log] [blame]
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import jdk.nashorn.internal.runtime.PropertyMap;
/**
* Manages constants needed by code generation. Objects are maintained in an
* interning maps to remove duplicates.
*/
final class ConstantData {
/** Constant table. */
final List<Object> constants;
/** Constant table string interning map. */
final Map<String, Integer> stringMap;
/** Constant table object interning map. */
final Map<Object, Integer> objectMap;
private static class ArrayWrapper {
private final Object array;
private final int hashCode;
public ArrayWrapper(final Object array) {
this.array = array;
this.hashCode = calcHashCode();
}
/**
* Calculate a shallow hashcode for the array.
* @return Hashcode with elements factored in.
*/
private int calcHashCode() {
final Class<?> cls = array.getClass();
if (!cls.getComponentType().isPrimitive()) {
return Arrays.hashCode((Object[])array);
} else if (cls == double[].class) {
return Arrays.hashCode((double[])array);
} if (cls == long[].class) {
return Arrays.hashCode((long[])array);
} if (cls == int[].class) {
return Arrays.hashCode((int[])array);
}
throw new AssertionError("ConstantData doesn't support " + cls);
}
@Override
public boolean equals(final Object other) {
if (!(other instanceof ArrayWrapper)) {
return false;
}
final Object otherArray = ((ArrayWrapper)other).array;
if (array == otherArray) {
return true;
}
final Class<?> cls = array.getClass();
if (cls == otherArray.getClass()) {
if (!cls.getComponentType().isPrimitive()) {
return Arrays.equals((Object[])array, (Object[])otherArray);
} else if (cls == double[].class) {
return Arrays.equals((double[])array, (double[])otherArray);
} else if (cls == long[].class) {
return Arrays.equals((long[])array, (long[])otherArray);
} else if (cls == int[].class) {
return Arrays.equals((int[])array, (int[])otherArray);
}
}
return false;
}
@Override
public int hashCode() {
return hashCode;
}
}
/**
* {@link PropertyMap} wrapper class that provides implementations for the {@code hashCode} and {@code equals}
* methods that are based on the map layout. {@code PropertyMap} itself inherits the identity based implementations
* from {@code java.lang.Object}.
*/
private static class PropertyMapWrapper {
private final PropertyMap propertyMap;
private final int hashCode;
public PropertyMapWrapper(final PropertyMap map) {
this.hashCode = Arrays.hashCode(map.getProperties()) + 31 * Objects.hashCode(map.getClassName());
this.propertyMap = map;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(final Object other) {
if (!(other instanceof PropertyMapWrapper)) {
return false;
}
final PropertyMap otherMap = ((PropertyMapWrapper) other).propertyMap;
return propertyMap == otherMap
|| (Arrays.equals(propertyMap.getProperties(), otherMap.getProperties())
&& Objects.equals(propertyMap.getClassName(), otherMap.getClassName()));
}
}
/**
* Constructor
*/
ConstantData() {
this.constants = new ArrayList<>();
this.stringMap = new HashMap<>();
this.objectMap = new HashMap<>();
}
/**
* Add a string to the constant data
*
* @param string the string to add
* @return the index in the constant pool that the string was given
*/
public int add(final String string) {
final Integer value = stringMap.get(string);
if (value != null) {
return value.intValue();
}
constants.add(string);
final int index = constants.size() - 1;
stringMap.put(string, index);
return index;
}
/**
* Add an object to the constant data
*
* @param object the string to add
* @return the index in the constant pool that the object was given
*/
public int add(final Object object) {
assert object != null;
final Object entry;
if (object.getClass().isArray()) {
entry = new ArrayWrapper(object);
} else if (object instanceof PropertyMap) {
entry = new PropertyMapWrapper((PropertyMap) object);
} else {
entry = object;
}
final Integer value = objectMap.get(entry);
if (value != null) {
return value.intValue();
}
constants.add(object);
final int index = constants.size() - 1;
objectMap.put(entry, index);
return index;
}
Object[] toArray() {
return constants.toArray();
}
}