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