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 
  26 package jdk.nashorn.internal.codegen;
  27 
  28 import jdk.nashorn.internal.runtime.Property;
  29 import jdk.nashorn.internal.runtime.PropertyMap;
  30 
  31 import java.util.ArrayList;
  32 import java.util.Arrays;
  33 import java.util.HashMap;
  34 import java.util.List;
  35 import java.util.Map;
  36 
  37 /**
  38  * Manages constants needed by code generation.  Objects are maintained in an
  39  * interning maps to remove duplicates.
  40  */
  41 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 == Object[].class) {
  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 == Object[].class) {
  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             int hash = 0;
 126             for (final Property property : map.getProperties()) {
 127                 hash = hash << 7 ^ hash >> 7;
 128                 hash ^= property.hashCode();
 129             }
 130             this.hashCode = hash;
 131             this.propertyMap = map;
 132         }
 133 
 134         @Override
 135         public int hashCode() {
 136             return hashCode;
 137         }
 138 
 139         @Override
 140         public boolean equals(final Object other) {
 141             if (!(other instanceof PropertyMapWrapper)) {
 142                 return false;
 143             }
 144 
 145             final Property[] ownProperties = propertyMap.getProperties();
 146             final Property[] otherProperties = ((PropertyMapWrapper) other).propertyMap.getProperties();
 147 
 148             return Arrays.equals(ownProperties, otherProperties);
 149         }
 150     }
 151 
 152     /**
 153      * Constructor
 154      */
 155     ConstantData() {
 156         this.constants = new ArrayList<>();
 157         this.stringMap = new HashMap<>();
 158         this.objectMap = new HashMap<>();
 159     }
 160 
 161     /**
 162      * Add a string to the constant data
 163      *
 164      * @param string the string to add
 165      * @return the index in the constant pool that the string was given
 166      */
 167     public int add(final String string) {
 168         final Integer value = stringMap.get(string);
 169 
 170         if (value != null) {
 171             return value.intValue();
 172         }
 173 
 174         constants.add(string);
 175         final int index = constants.size() - 1;
 176         stringMap.put(string, index);
 177 
 178         return index;
 179     }
 180 
 181     /**
 182      * Add an object to the constant data
 183      *
 184      * @param object the string to add
 185      * @return the index in the constant pool that the object was given
 186      */
 187     public int add(final Object object) {
 188         final Object  entry;
 189         if (object.getClass().isArray()) {
 190             entry = new ArrayWrapper(object);
 191         } else if (object instanceof PropertyMap) {
 192             entry = new PropertyMapWrapper((PropertyMap) object);
 193         } else {
 194             entry = object;
 195         }
 196         final Integer value = objectMap.get(entry);
 197 
 198         if (value != null) {
 199             return value.intValue();
 200         }
 201 
 202         constants.add(object);
 203         final int index = constants.size() - 1;
 204         objectMap.put(entry, index);
 205 
 206         return index;
 207     }
 208 
 209     Object[] toArray() {
 210         return constants.toArray();
 211     }
 212 }