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
26package jdk.nashorn.internal.runtime;
27
28import static jdk.nashorn.internal.lookup.Lookup.MH;
29
30import java.lang.invoke.MethodHandle;
31import java.lang.invoke.MethodHandles;
32
33/**
34 * Spill property
35 */
36public 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}
223