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 
Accessors(final int slot)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 
ensure(final int slot)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 
getCached(final int slot, final boolean isPrimitive, final boolean isGetter)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 
primordial(final boolean isPrimitive, final boolean isGetter)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 
getOrCreate(final boolean isPrimitive, final boolean isGetter)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 
setInner(final boolean isPrimitive, final boolean isGetter, final MethodHandle mh)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 
getInner(final boolean isPrimitive, final boolean isGetter)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 
primitiveGetter(final int slot, final int flags)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     }
primitiveSetter(final int slot, final int flags)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     }
objectGetter(final int slot)147     private static MethodHandle objectGetter(final int slot) {
148         return Accessors.getCached(slot, false, true);
149     }
objectSetter(final int slot)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      */
SpillProperty(final String key, final int flags, final int slot)161     public SpillProperty(final String 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      */
SpillProperty(final String key, final int flags, final int slot, final Class<?> initialType)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 
SpillProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue)177     SpillProperty(final String 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      */
SpillProperty(final SpillProperty property)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      */
SpillProperty(final SpillProperty property, final Class<?> newType)195     protected SpillProperty(final SpillProperty property, final Class<?> newType) {
196         super(property, newType);
197     }
198 
199     @Override
copy()200     public Property copy() {
201         return new SpillProperty(this);
202     }
203 
204     @Override
copy(final Class<?> newType)205     public Property copy(final Class<?> newType) {
206         return new SpillProperty(this, newType);
207     }
208 
209     @Override
isSpill()210     public boolean isSpill() {
211         return true;
212     }
213 
214     @Override
initMethodHandles(final Class<?> structure)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 }
223