1 /*
   2  * Copyright (c) 2010, 2014, 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.runtime;
  27 
  28 import static jdk.nashorn.internal.lookup.Lookup.MH;
  29 
  30 import java.lang.invoke.MethodHandle;
  31 import java.lang.invoke.MethodHandles;
  32 
  33 /**
  34  * Spill property
  35  */
  36 public class SpillProperty extends AccessorProperty {
  37     private static final long serialVersionUID = 3028496245198669460L;
  38 
  39     private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
  40 
  41     private static final MethodHandle PARRAY_GETTER = MH.asType(MH.getter(LOOKUP, ScriptObject.class, "primitiveSpill",  long[].class), MH.type(long[].class, Object.class));
  42     private static final MethodHandle OARRAY_GETTER = MH.asType(MH.getter(LOOKUP, ScriptObject.class, "objectSpill",  Object[].class), MH.type(Object[].class, Object.class));
  43 
  44     private static final MethodHandle OBJECT_GETTER    = MH.filterArguments(MH.arrayElementGetter(Object[].class), 0, OARRAY_GETTER);
  45     private static final MethodHandle PRIMITIVE_GETTER = MH.filterArguments(MH.arrayElementGetter(long[].class), 0, PARRAY_GETTER);
  46     private static final MethodHandle OBJECT_SETTER    = MH.filterArguments(MH.arrayElementSetter(Object[].class), 0, OARRAY_GETTER);
  47     private static final MethodHandle PRIMITIVE_SETTER = MH.filterArguments(MH.arrayElementSetter(long[].class), 0, PARRAY_GETTER);
  48 
  49     private static class Accessors {
  50         private MethodHandle objectGetter;
  51         private MethodHandle objectSetter;
  52         private MethodHandle primitiveGetter;
  53         private MethodHandle primitiveSetter;
  54 
  55         private final int slot;
  56         private final MethodHandle ensureSpillSize;
  57 
  58         private static Accessors ACCESSOR_CACHE[] = new Accessors[512];
  59 
  60         //private static final Map<Integer, Reference<Accessors>> ACCESSOR_CACHE = Collections.synchronizedMap(new WeakHashMap<Integer, Reference<Accessors>>());
  61 
  62         Accessors(final int slot) {
  63             assert slot >= 0;
  64             this.slot = slot;
  65             this.ensureSpillSize = MH.asType(MH.insertArguments(ScriptObject.ENSURE_SPILL_SIZE, 1, slot), MH.type(Object.class, Object.class));
  66         }
  67 
  68         private static void ensure(final int slot) {
  69             int len = ACCESSOR_CACHE.length;
  70             if (slot >= len) {
  71                 do {
  72                     len *= 2;
  73                 } while (slot >= len);
  74                 final Accessors newCache[] = new Accessors[len];
  75                 System.arraycopy(ACCESSOR_CACHE, 0, newCache, 0, ACCESSOR_CACHE.length);
  76                 ACCESSOR_CACHE = newCache;
  77             }
  78         }
  79 
  80         static MethodHandle getCached(final int slot, final boolean isPrimitive, final boolean isGetter) {
  81             //Reference<Accessors> ref = ACCESSOR_CACHE.get(slot);
  82             ensure(slot);
  83             Accessors acc = ACCESSOR_CACHE[slot];
  84             if (acc == null) {
  85                 acc = new Accessors(slot);
  86                 ACCESSOR_CACHE[slot] = acc;
  87             }
  88 
  89             return acc.getOrCreate(isPrimitive, isGetter);
  90         }
  91 
  92         private static MethodHandle primordial(final boolean isPrimitive, final boolean isGetter) {
  93             if (isPrimitive) {
  94                 return isGetter ? PRIMITIVE_GETTER : PRIMITIVE_SETTER;
  95             }
  96             return isGetter ? OBJECT_GETTER : OBJECT_SETTER;
  97         }
  98 
  99         MethodHandle getOrCreate(final boolean isPrimitive, final boolean isGetter) {
 100             MethodHandle accessor;
 101 
 102             accessor = getInner(isPrimitive, isGetter);
 103             if (accessor != null) {
 104                 return accessor;
 105             }
 106 
 107             accessor = primordial(isPrimitive, isGetter);
 108             accessor = MH.insertArguments(accessor, 1, slot);
 109             if (!isGetter) {
 110                 accessor = MH.filterArguments(accessor, 0, ensureSpillSize);
 111             }
 112             setInner(isPrimitive, isGetter, accessor);
 113 
 114             return accessor;
 115         }
 116 
 117         void setInner(final boolean isPrimitive, final boolean isGetter, final MethodHandle mh) {
 118             if (isPrimitive) {
 119                 if (isGetter) {
 120                     primitiveGetter = mh;
 121                 } else {
 122                     primitiveSetter = mh;
 123                 }
 124             } else {
 125                 if (isGetter) {
 126                     objectGetter = mh;
 127                 } else {
 128                     objectSetter = mh;
 129                 }
 130             }
 131         }
 132 
 133         MethodHandle getInner(final boolean isPrimitive, final boolean isGetter) {
 134             if (isPrimitive) {
 135                 return isGetter ? primitiveGetter : primitiveSetter;
 136             }
 137             return isGetter ? objectGetter : objectSetter;
 138         }
 139     }
 140 
 141     private static MethodHandle primitiveGetter(final int slot, final int flags) {
 142         return (flags & DUAL_FIELDS) == DUAL_FIELDS ? Accessors.getCached(slot, true, true) : null;
 143     }
 144     private static MethodHandle primitiveSetter(final int slot, final int flags) {
 145         return (flags & DUAL_FIELDS) == DUAL_FIELDS ? Accessors.getCached(slot, true, false) : null;
 146     }
 147     private static MethodHandle objectGetter(final int slot) {
 148         return Accessors.getCached(slot, false, true);
 149     }
 150     private static MethodHandle objectSetter(final int slot) {
 151         return Accessors.getCached(slot, false, false);
 152     }
 153 
 154     /**
 155      * Constructor for spill properties. Array getters and setters will be created on demand.
 156      *
 157      * @param key    the property key
 158      * @param flags  the property flags
 159      * @param slot   spill slot
 160      */
 161     public SpillProperty(final Object key, final int flags, final int slot) {
 162         super(key, flags, slot, primitiveGetter(slot, flags), primitiveSetter(slot, flags), objectGetter(slot), objectSetter(slot));
 163     }
 164 
 165     /**
 166      * Constructor for spill properties with an initial type.
 167      * @param key         the property key
 168      * @param flags       the property flags
 169      * @param slot        spill slot
 170      * @param initialType initial type
 171      */
 172     public SpillProperty(final String key, final int flags, final int slot, final Class<?> initialType) {
 173         this(key, flags, slot);
 174         setType(hasDualFields() ? initialType : Object.class);
 175     }
 176 
 177     SpillProperty(final Object key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
 178         this(key, flags, slot);
 179         setInitialValue(owner, initialValue);
 180     }
 181 
 182     /**
 183      * Copy constructor
 184      * @param property other property
 185      */
 186     protected SpillProperty(final SpillProperty property) {
 187         super(property);
 188     }
 189 
 190     /**
 191      * Copy constructor
 192      * @param newType new type
 193      * @param property other property
 194      */
 195     protected SpillProperty(final SpillProperty property, final Class<?> newType) {
 196         super(property, newType);
 197     }
 198 
 199     @Override
 200     public Property copy() {
 201         return new SpillProperty(this);
 202     }
 203 
 204     @Override
 205     public Property copy(final Class<?> newType) {
 206         return new SpillProperty(this, newType);
 207     }
 208 
 209     @Override
 210     public boolean isSpill() {
 211         return true;
 212     }
 213 
 214     @Override
 215     void initMethodHandles(final Class<?> structure) {
 216         final int slot  = getSlot();
 217         primitiveGetter = primitiveGetter(slot, getFlags());
 218         primitiveSetter = primitiveSetter(slot, getFlags());
 219         objectGetter    = objectGetter(slot);
 220         objectSetter    = objectSetter(slot);
 221     }
 222 }