Wed, 12 Mar 2014 11:26:00 +0100
8021350: Share script classes between threads/globals within context
Reviewed-by: lagergren, sundar
jlaskey@3 | 1 | /* |
jlaskey@7 | 2 | * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. |
jlaskey@3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
jlaskey@3 | 4 | * |
jlaskey@3 | 5 | * This code is free software; you can redistribute it and/or modify it |
jlaskey@3 | 6 | * under the terms of the GNU General Public License version 2 only, as |
jlaskey@3 | 7 | * published by the Free Software Foundation. Oracle designates this |
jlaskey@3 | 8 | * particular file as subject to the "Classpath" exception as provided |
jlaskey@3 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
jlaskey@3 | 10 | * |
jlaskey@3 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
jlaskey@3 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
jlaskey@3 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
jlaskey@3 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
jlaskey@3 | 15 | * accompanied this code). |
jlaskey@3 | 16 | * |
jlaskey@3 | 17 | * You should have received a copy of the GNU General Public License version |
jlaskey@3 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
jlaskey@3 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
jlaskey@3 | 20 | * |
jlaskey@3 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
jlaskey@3 | 22 | * or visit www.oracle.com if you need additional information or have any |
jlaskey@3 | 23 | * questions. |
jlaskey@3 | 24 | */ |
jlaskey@3 | 25 | |
jlaskey@3 | 26 | package jdk.nashorn.internal.runtime; |
jlaskey@3 | 27 | |
lagergren@96 | 28 | import static jdk.nashorn.internal.codegen.ObjectClassGenerator.ACCESSOR_TYPES; |
lagergren@96 | 29 | import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS; |
lagergren@96 | 30 | import static jdk.nashorn.internal.codegen.ObjectClassGenerator.LOG; |
lagergren@96 | 31 | import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY; |
lagergren@96 | 32 | import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_TYPE; |
lagergren@96 | 33 | import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createGetter; |
lagergren@96 | 34 | import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createGuardBoxedPrimitiveSetter; |
lagergren@96 | 35 | import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createSetter; |
lagergren@96 | 36 | import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getAccessorType; |
lagergren@96 | 37 | import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getAccessorTypeIndex; |
lagergren@96 | 38 | import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getNumberOfAccessorTypes; |
sundar@133 | 39 | import static jdk.nashorn.internal.lookup.Lookup.MH; |
sundar@133 | 40 | import static jdk.nashorn.internal.lookup.MethodHandleFactory.stripName; |
jlaskey@3 | 41 | |
jlaskey@3 | 42 | import java.lang.invoke.MethodHandle; |
jlaskey@3 | 43 | import java.lang.invoke.MethodHandles; |
jlaskey@3 | 44 | import java.lang.invoke.MethodType; |
lagergren@96 | 45 | import jdk.nashorn.internal.codegen.ObjectClassGenerator; |
jlaskey@3 | 46 | import jdk.nashorn.internal.codegen.types.Type; |
sundar@133 | 47 | import jdk.nashorn.internal.lookup.Lookup; |
sundar@133 | 48 | import jdk.nashorn.internal.lookup.MethodHandleFactory; |
jlaskey@3 | 49 | |
jlaskey@3 | 50 | /** |
jlaskey@3 | 51 | * An AccessorProperty is the most generic property type. An AccessorProperty is |
jlaskey@3 | 52 | * represented as fields in a ScriptObject class. |
jlaskey@3 | 53 | */ |
sundar@476 | 54 | public final class AccessorProperty extends Property { |
attila@224 | 55 | private static final MethodHandles.Lookup lookup = MethodHandles.lookup(); |
jlaskey@3 | 56 | private static final MethodHandle REPLACE_MAP = findOwnMH("replaceMap", Object.class, Object.class, PropertyMap.class, String.class, Class.class, Class.class); |
jlaskey@3 | 57 | |
jlaskey@3 | 58 | private static final int NOOF_TYPES = getNumberOfAccessorTypes(); |
jlaskey@3 | 59 | |
attila@224 | 60 | /** |
attila@224 | 61 | * Properties in different maps for the same structure class will share their field getters and setters. This could |
attila@224 | 62 | * be further extended to other method handles that are looked up in the AccessorProperty constructor, but right now |
attila@224 | 63 | * these are the most frequently retrieved ones, and lookup of method handle natives only registers in the profiler |
attila@224 | 64 | * for them. |
attila@224 | 65 | */ |
attila@224 | 66 | private static ClassValue<GettersSetters> GETTERS_SETTERS = new ClassValue<GettersSetters>() { |
attila@224 | 67 | @Override |
attila@224 | 68 | protected GettersSetters computeValue(Class<?> structure) { |
attila@224 | 69 | return new GettersSetters(structure); |
attila@224 | 70 | } |
attila@224 | 71 | }; |
attila@224 | 72 | |
jlaskey@3 | 73 | /** Property getter cache */ |
jlaskey@3 | 74 | private MethodHandle[] getters = new MethodHandle[NOOF_TYPES]; |
jlaskey@3 | 75 | |
jlaskey@3 | 76 | private static final MethodType[] ACCESSOR_GETTER_TYPES = new MethodType[NOOF_TYPES]; |
jlaskey@3 | 77 | private static final MethodType[] ACCESSOR_SETTER_TYPES = new MethodType[NOOF_TYPES]; |
jlaskey@410 | 78 | private static final MethodType ACCESSOR_GETTER_PRIMITIVE_TYPE; |
jlaskey@410 | 79 | private static final MethodType ACCESSOR_SETTER_PRIMITIVE_TYPE; |
hannesw@291 | 80 | private static final MethodHandle SPILL_ELEMENT_GETTER; |
hannesw@291 | 81 | private static final MethodHandle SPILL_ELEMENT_SETTER; |
hannesw@291 | 82 | |
hannesw@291 | 83 | private static final int SPILL_CACHE_SIZE = 8; |
hannesw@291 | 84 | private static final MethodHandle[] SPILL_ACCESSORS = new MethodHandle[SPILL_CACHE_SIZE * 2]; |
hannesw@291 | 85 | |
hannesw@291 | 86 | static { |
jlaskey@410 | 87 | MethodType getterPrimitiveType = null; |
jlaskey@410 | 88 | MethodType setterPrimitiveType = null; |
jlaskey@410 | 89 | |
hannesw@291 | 90 | for (int i = 0; i < NOOF_TYPES; i++) { |
hannesw@291 | 91 | final Type type = ACCESSOR_TYPES.get(i); |
hannesw@291 | 92 | ACCESSOR_GETTER_TYPES[i] = MH.type(type.getTypeClass(), Object.class); |
hannesw@291 | 93 | ACCESSOR_SETTER_TYPES[i] = MH.type(void.class, Object.class, type.getTypeClass()); |
jlaskey@410 | 94 | |
jlaskey@410 | 95 | if (type == PRIMITIVE_TYPE) { |
jlaskey@410 | 96 | getterPrimitiveType = ACCESSOR_GETTER_TYPES[i]; |
jlaskey@410 | 97 | setterPrimitiveType = ACCESSOR_SETTER_TYPES[i]; |
jlaskey@410 | 98 | } |
hannesw@291 | 99 | } |
hannesw@291 | 100 | |
jlaskey@410 | 101 | ACCESSOR_GETTER_PRIMITIVE_TYPE = getterPrimitiveType; |
jlaskey@410 | 102 | ACCESSOR_SETTER_PRIMITIVE_TYPE = setterPrimitiveType; |
jlaskey@410 | 103 | |
jlaskey@410 | 104 | final MethodType spillGetterType = MethodType.methodType(Object[].class, Object.class); |
jlaskey@410 | 105 | final MethodHandle spillGetter = MH.asType(MH.getter(MethodHandles.lookup(), ScriptObject.class, "spill", Object[].class), spillGetterType); |
hannesw@291 | 106 | SPILL_ELEMENT_GETTER = MH.filterArguments(MH.arrayElementGetter(Object[].class), 0, spillGetter); |
hannesw@291 | 107 | SPILL_ELEMENT_SETTER = MH.filterArguments(MH.arrayElementSetter(Object[].class), 0, spillGetter); |
hannesw@291 | 108 | } |
jlaskey@3 | 109 | |
sundar@418 | 110 | /** |
sundar@418 | 111 | * Create a new accessor property. Factory method used by nasgen generated code. |
sundar@418 | 112 | * |
sundar@418 | 113 | * @param key {@link Property} key. |
sundar@418 | 114 | * @param propertyFlags {@link Property} flags. |
sundar@418 | 115 | * @param getter {@link Property} get accessor method. |
sundar@418 | 116 | * @param setter {@link Property} set accessor method. |
sundar@418 | 117 | * |
sundar@418 | 118 | * @return New {@link AccessorProperty} created. |
sundar@418 | 119 | */ |
sundar@418 | 120 | public static AccessorProperty create(final String key, final int propertyFlags, final MethodHandle getter, final MethodHandle setter) { |
sundar@418 | 121 | return new AccessorProperty(key, propertyFlags, -1, getter, setter); |
sundar@418 | 122 | } |
sundar@418 | 123 | |
jlaskey@3 | 124 | /** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */ |
jlaskey@3 | 125 | private MethodHandle primitiveGetter; |
jlaskey@3 | 126 | |
jlaskey@3 | 127 | /** Seed setter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */ |
jlaskey@3 | 128 | private MethodHandle primitiveSetter; |
jlaskey@3 | 129 | |
jlaskey@3 | 130 | /** Seed getter for the Object version of this field */ |
jlaskey@3 | 131 | private MethodHandle objectGetter; |
jlaskey@3 | 132 | |
jlaskey@3 | 133 | /** Seed setter for the Object version of this field */ |
jlaskey@3 | 134 | private MethodHandle objectSetter; |
jlaskey@3 | 135 | |
jlaskey@3 | 136 | /** |
jlaskey@3 | 137 | * Current type of this object, in object only mode, this is an Object.class. In dual-fields mode |
jlaskey@3 | 138 | * null means undefined, and primitive types are allowed. The reason a special type is used for |
jlaskey@3 | 139 | * undefined, is that are no bits left to represent it in primitive types |
jlaskey@3 | 140 | */ |
jlaskey@3 | 141 | private Class<?> currentType; |
jlaskey@3 | 142 | |
jlaskey@3 | 143 | /** |
hannesw@769 | 144 | * Delegate constructor for bound properties. This is used for properties created by |
hannesw@769 | 145 | * {@link ScriptRuntime#mergeScope} and the Nashorn {@code Object.bindProperties} method. |
hannesw@769 | 146 | * The former is used to add a script's defined globals to the current global scope while |
hannesw@769 | 147 | * still storing them in a JO-prefixed ScriptObject class. |
hannesw@769 | 148 | * |
hannesw@769 | 149 | * <p>All properties created by this constructor have the {@link #IS_BOUND} flag set.</p> |
jlaskey@3 | 150 | * |
jlaskey@3 | 151 | * @param property accessor property to rebind |
sundar@423 | 152 | * @param delegate delegate object to rebind receiver to |
jlaskey@3 | 153 | */ |
sundar@476 | 154 | AccessorProperty(final AccessorProperty property, final Object delegate) { |
hannesw@291 | 155 | super(property); |
jlaskey@3 | 156 | |
hannesw@291 | 157 | this.primitiveGetter = bindTo(property.primitiveGetter, delegate); |
hannesw@291 | 158 | this.primitiveSetter = bindTo(property.primitiveSetter, delegate); |
hannesw@415 | 159 | this.objectGetter = bindTo(property.ensureObjectGetter(), delegate); |
hannesw@415 | 160 | this.objectSetter = bindTo(property.ensureObjectSetter(), delegate); |
jlaskey@3 | 161 | |
hannesw@769 | 162 | // Properties created this way are bound to a delegate |
hannesw@769 | 163 | this.flags |= IS_BOUND; |
jlaskey@3 | 164 | setCurrentType(property.getCurrentType()); |
jlaskey@3 | 165 | } |
jlaskey@3 | 166 | |
jlaskey@3 | 167 | /** |
hannesw@291 | 168 | * Constructor for spill properties. Array getters and setters will be created on demand. |
hannesw@291 | 169 | * |
hannesw@291 | 170 | * @param key the property key |
hannesw@291 | 171 | * @param flags the property flags |
hannesw@291 | 172 | * @param slot spill slot |
hannesw@291 | 173 | */ |
hannesw@291 | 174 | public AccessorProperty(final String key, final int flags, final int slot) { |
hannesw@291 | 175 | super(key, flags, slot); |
hannesw@291 | 176 | assert (flags & IS_SPILL) == IS_SPILL; |
hannesw@291 | 177 | |
hannesw@291 | 178 | setCurrentType(Object.class); |
hannesw@291 | 179 | } |
hannesw@291 | 180 | |
hannesw@291 | 181 | /** |
jlaskey@3 | 182 | * Constructor. Similar to the constructor with both primitive getters and setters, the difference |
jlaskey@3 | 183 | * here being that only one getter and setter (setter is optional for non writable fields) is given |
jlaskey@3 | 184 | * to the constructor, and the rest are created from those. Used e.g. by Nasgen classes |
jlaskey@3 | 185 | * |
jlaskey@3 | 186 | * @param key the property key |
jlaskey@3 | 187 | * @param flags the property flags |
jlaskey@80 | 188 | * @param slot the property field number or spill slot |
jlaskey@3 | 189 | * @param getter the property getter |
jlaskey@3 | 190 | * @param setter the property setter or null if non writable, non configurable |
jlaskey@3 | 191 | */ |
sundar@476 | 192 | AccessorProperty(final String key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) { |
jlaskey@80 | 193 | super(key, flags, slot); |
jlaskey@3 | 194 | |
jlaskey@3 | 195 | // we don't need to prep the setters these will never be invalidated as this is a nasgen |
jlaskey@3 | 196 | // or known type getter/setter. No invalidations will take place |
jlaskey@3 | 197 | |
jlaskey@3 | 198 | final Class<?> getterType = getter.type().returnType(); |
jlaskey@3 | 199 | final Class<?> setterType = setter == null ? null : setter.type().parameterType(1); |
jlaskey@3 | 200 | |
jlaskey@3 | 201 | assert setterType == null || setterType == getterType; |
jlaskey@3 | 202 | |
jlaskey@3 | 203 | if (getterType.isPrimitive()) { |
jlaskey@3 | 204 | for (int i = 0; i < NOOF_TYPES; i++) { |
jlaskey@3 | 205 | getters[i] = MH.asType( |
jlaskey@3 | 206 | Lookup.filterReturnType( |
jlaskey@3 | 207 | getter, |
jlaskey@3 | 208 | getAccessorType(i).getTypeClass()), |
jlaskey@3 | 209 | ACCESSOR_GETTER_TYPES[i]); |
jlaskey@3 | 210 | } |
jlaskey@3 | 211 | } else { |
jlaskey@410 | 212 | objectGetter = getter.type() != Lookup.GET_OBJECT_TYPE ? MH.asType(getter, Lookup.GET_OBJECT_TYPE) : getter; |
jlaskey@410 | 213 | objectSetter = setter != null && setter.type() != Lookup.SET_OBJECT_TYPE ? MH.asType(setter, Lookup.SET_OBJECT_TYPE) : setter; |
jlaskey@3 | 214 | } |
jlaskey@3 | 215 | |
jlaskey@3 | 216 | setCurrentType(getterType); |
jlaskey@3 | 217 | } |
jlaskey@3 | 218 | |
attila@224 | 219 | private static class GettersSetters { |
attila@224 | 220 | final MethodHandle[] getters; |
attila@224 | 221 | final MethodHandle[] setters; |
attila@224 | 222 | |
attila@224 | 223 | public GettersSetters(Class<?> structure) { |
attila@224 | 224 | final int fieldCount = ObjectClassGenerator.getFieldCount(structure); |
attila@224 | 225 | getters = new MethodHandle[fieldCount]; |
attila@224 | 226 | setters = new MethodHandle[fieldCount]; |
attila@224 | 227 | for(int i = 0; i < fieldCount; ++i) { |
attila@224 | 228 | final String fieldName = ObjectClassGenerator.getFieldName(i, Type.OBJECT); |
jlaskey@410 | 229 | getters[i] = MH.asType(MH.getter(lookup, structure, fieldName, Type.OBJECT.getTypeClass()), Lookup.GET_OBJECT_TYPE); |
jlaskey@410 | 230 | setters[i] = MH.asType(MH.setter(lookup, structure, fieldName, Type.OBJECT.getTypeClass()), Lookup.SET_OBJECT_TYPE); |
attila@224 | 231 | } |
attila@224 | 232 | } |
attila@224 | 233 | } |
attila@224 | 234 | |
jlaskey@3 | 235 | /** |
jlaskey@80 | 236 | * Constructor for dual field AccessorPropertys. |
jlaskey@80 | 237 | * |
jlaskey@80 | 238 | * @param key property key |
jlaskey@80 | 239 | * @param flags property flags |
jlaskey@80 | 240 | * @param structure structure for objects associated with this property |
jlaskey@80 | 241 | * @param slot property field number or spill slot |
jlaskey@80 | 242 | */ |
jlaskey@80 | 243 | public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot) { |
jlaskey@80 | 244 | super(key, flags, slot); |
jlaskey@80 | 245 | |
jlaskey@80 | 246 | /* |
jlaskey@80 | 247 | * primitiveGetter and primitiveSetter are only used in dual fields mode. Setting them to null also |
jlaskey@80 | 248 | * works in dual field mode, it only means that the property never has a primitive |
jlaskey@80 | 249 | * representation. |
jlaskey@80 | 250 | */ |
jlaskey@80 | 251 | primitiveGetter = null; |
jlaskey@80 | 252 | primitiveSetter = null; |
jlaskey@80 | 253 | |
jlaskey@80 | 254 | if (isParameter() && hasArguments()) { |
attila@422 | 255 | final MethodHandle arguments = MH.getter(lookup, structure, "arguments", ScriptObject.class); |
jlaskey@80 | 256 | |
attila@422 | 257 | objectGetter = MH.asType(MH.insertArguments(MH.filterArguments(ScriptObject.GET_ARGUMENT.methodHandle(), 0, arguments), 1, slot), Lookup.GET_OBJECT_TYPE); |
attila@422 | 258 | objectSetter = MH.asType(MH.insertArguments(MH.filterArguments(ScriptObject.SET_ARGUMENT.methodHandle(), 0, arguments), 1, slot), Lookup.SET_OBJECT_TYPE); |
jlaskey@80 | 259 | } else { |
attila@224 | 260 | final GettersSetters gs = GETTERS_SETTERS.get(structure); |
attila@224 | 261 | objectGetter = gs.getters[slot]; |
attila@224 | 262 | objectSetter = gs.setters[slot]; |
jlaskey@80 | 263 | |
jlaskey@80 | 264 | if (!OBJECT_FIELDS_ONLY) { |
jlaskey@410 | 265 | final String fieldNamePrimitive = ObjectClassGenerator.getFieldName(slot, PRIMITIVE_TYPE); |
jlaskey@410 | 266 | final Class<?> typeClass = PRIMITIVE_TYPE.getTypeClass(); |
jlaskey@410 | 267 | primitiveGetter = MH.asType(MH.getter(lookup, structure, fieldNamePrimitive, typeClass), ACCESSOR_GETTER_PRIMITIVE_TYPE); |
jlaskey@410 | 268 | primitiveSetter = MH.asType(MH.setter(lookup, structure, fieldNamePrimitive, typeClass), ACCESSOR_SETTER_PRIMITIVE_TYPE); |
jlaskey@80 | 269 | } |
jlaskey@80 | 270 | } |
jlaskey@80 | 271 | |
jlaskey@80 | 272 | Class<?> initialType = null; |
jlaskey@80 | 273 | |
jlaskey@80 | 274 | if (OBJECT_FIELDS_ONLY || isAlwaysObject()) { |
jlaskey@80 | 275 | initialType = Object.class; |
jlaskey@80 | 276 | } else if (!canBePrimitive()) { |
jlaskey@80 | 277 | info(key + " cannot be primitive"); |
jlaskey@80 | 278 | initialType = Object.class; |
jlaskey@80 | 279 | } else { |
jlaskey@80 | 280 | info(key + " CAN be primitive"); |
jlaskey@80 | 281 | if (!canBeUndefined()) { |
jlaskey@80 | 282 | info(key + " is always defined"); |
jlaskey@80 | 283 | initialType = int.class; //double works too for less type invalidation, but this requires experimentation, e.g. var x = 17; x += 2 will turn it into double now because of lack of range analysis |
jlaskey@80 | 284 | } |
jlaskey@80 | 285 | } |
jlaskey@80 | 286 | |
jlaskey@80 | 287 | // is always object means "is never initialized to undefined, and always of object type |
jlaskey@80 | 288 | setCurrentType(initialType); |
jlaskey@80 | 289 | } |
jlaskey@80 | 290 | |
jlaskey@80 | 291 | /** |
jlaskey@3 | 292 | * Copy constructor |
jlaskey@3 | 293 | * |
jlaskey@3 | 294 | * @param property source property |
jlaskey@3 | 295 | */ |
jlaskey@3 | 296 | protected AccessorProperty(final AccessorProperty property) { |
jlaskey@3 | 297 | super(property); |
jlaskey@3 | 298 | |
jlaskey@3 | 299 | this.getters = property.getters; |
jlaskey@3 | 300 | this.primitiveGetter = property.primitiveGetter; |
jlaskey@3 | 301 | this.primitiveSetter = property.primitiveSetter; |
jlaskey@3 | 302 | this.objectGetter = property.objectGetter; |
jlaskey@3 | 303 | this.objectSetter = property.objectSetter; |
jlaskey@3 | 304 | |
jlaskey@3 | 305 | setCurrentType(property.getCurrentType()); |
jlaskey@3 | 306 | } |
jlaskey@3 | 307 | |
jlaskey@3 | 308 | private static MethodHandle bindTo(final MethodHandle mh, final Object receiver) { |
jlaskey@3 | 309 | if (mh == null) { |
jlaskey@3 | 310 | return null; |
jlaskey@3 | 311 | } |
jlaskey@3 | 312 | |
jlaskey@3 | 313 | return MH.dropArguments(MH.bindTo(mh, receiver), 0, Object.class); |
jlaskey@3 | 314 | } |
jlaskey@3 | 315 | |
jlaskey@3 | 316 | @Override |
jlaskey@3 | 317 | protected Property copy() { |
jlaskey@3 | 318 | return new AccessorProperty(this); |
jlaskey@3 | 319 | } |
jlaskey@3 | 320 | |
jlaskey@3 | 321 | @Override |
sundar@377 | 322 | public void setObjectValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) { |
hannesw@291 | 323 | if (isSpill()) { |
hannesw@291 | 324 | self.spill[getSlot()] = value; |
hannesw@291 | 325 | } else { |
hannesw@291 | 326 | try { |
hannesw@291 | 327 | getSetter(Object.class, self.getMap()).invokeExact((Object)self, value); |
hannesw@291 | 328 | } catch (final Error|RuntimeException e) { |
hannesw@291 | 329 | throw e; |
hannesw@291 | 330 | } catch (final Throwable e) { |
hannesw@291 | 331 | throw new RuntimeException(e); |
hannesw@291 | 332 | } |
hannesw@291 | 333 | } |
hannesw@291 | 334 | } |
hannesw@291 | 335 | |
hannesw@291 | 336 | @Override |
sundar@377 | 337 | public Object getObjectValue(final ScriptObject self, final ScriptObject owner) { |
hannesw@291 | 338 | if (isSpill()) { |
hannesw@291 | 339 | return self.spill[getSlot()]; |
lagergren@295 | 340 | } |
lagergren@295 | 341 | |
lagergren@295 | 342 | try { |
lagergren@295 | 343 | return getGetter(Object.class).invokeExact((Object)self); |
lagergren@295 | 344 | } catch (final Error|RuntimeException e) { |
lagergren@295 | 345 | throw e; |
lagergren@295 | 346 | } catch (final Throwable e) { |
lagergren@295 | 347 | throw new RuntimeException(e); |
hannesw@291 | 348 | } |
hannesw@291 | 349 | } |
hannesw@291 | 350 | |
hannesw@415 | 351 | // Spill getters and setters are lazily initialized, see JDK-8011630 |
hannesw@415 | 352 | private MethodHandle ensureObjectGetter() { |
hannesw@291 | 353 | if (isSpill() && objectGetter == null) { |
hannesw@291 | 354 | objectGetter = getSpillGetter(); |
hannesw@291 | 355 | } |
hannesw@415 | 356 | return objectGetter; |
hannesw@415 | 357 | } |
hannesw@415 | 358 | |
hannesw@415 | 359 | private MethodHandle ensureObjectSetter() { |
hannesw@415 | 360 | if (isSpill() && objectSetter == null) { |
hannesw@415 | 361 | objectSetter = getSpillSetter(); |
hannesw@415 | 362 | } |
hannesw@415 | 363 | return objectSetter; |
hannesw@415 | 364 | } |
hannesw@415 | 365 | |
hannesw@415 | 366 | @Override |
hannesw@415 | 367 | public MethodHandle getGetter(final Class<?> type) { |
jlaskey@3 | 368 | final int i = getAccessorTypeIndex(type); |
hannesw@415 | 369 | ensureObjectGetter(); |
hannesw@415 | 370 | |
jlaskey@3 | 371 | if (getters[i] == null) { |
jlaskey@3 | 372 | getters[i] = debug( |
jlaskey@410 | 373 | createGetter(currentType, type, primitiveGetter, objectGetter), |
jlaskey@410 | 374 | currentType, type, "get"); |
jlaskey@3 | 375 | } |
jlaskey@3 | 376 | |
hannesw@291 | 377 | return getters[i]; |
jlaskey@3 | 378 | } |
jlaskey@3 | 379 | |
jlaskey@3 | 380 | private Property getWiderProperty(final Class<?> type) { |
jlaskey@3 | 381 | final AccessorProperty newProperty = new AccessorProperty(this); |
jlaskey@3 | 382 | newProperty.invalidate(type); |
jlaskey@3 | 383 | return newProperty; |
jlaskey@3 | 384 | } |
jlaskey@3 | 385 | |
jlaskey@3 | 386 | private PropertyMap getWiderMap(final PropertyMap oldMap, final Property newProperty) { |
jlaskey@3 | 387 | final PropertyMap newMap = oldMap.replaceProperty(this, newProperty); |
jlaskey@3 | 388 | assert oldMap.size() > 0; |
jlaskey@3 | 389 | assert newMap.size() == oldMap.size(); |
jlaskey@3 | 390 | return newMap; |
jlaskey@3 | 391 | } |
jlaskey@3 | 392 | |
jlaskey@3 | 393 | // the final three arguments are for debug printout purposes only |
jlaskey@3 | 394 | @SuppressWarnings("unused") |
jlaskey@3 | 395 | private static Object replaceMap(final Object sobj, final PropertyMap newMap, final String key, final Class<?> oldType, final Class<?> newType) { |
jlaskey@3 | 396 | if (DEBUG_FIELDS) { |
jlaskey@3 | 397 | final PropertyMap oldMap = ((ScriptObject)sobj).getMap(); |
jlaskey@3 | 398 | info("Type change for '" + key + "' " + oldType + "=>" + newType); |
jlaskey@3 | 399 | finest("setting map " + sobj + " from " + Debug.id(oldMap) + " to " + Debug.id(newMap) + " " + oldMap + " => " + newMap); |
jlaskey@3 | 400 | } |
jlaskey@3 | 401 | ((ScriptObject)sobj).setMap(newMap); |
jlaskey@3 | 402 | return sobj; |
jlaskey@3 | 403 | } |
jlaskey@3 | 404 | |
jlaskey@3 | 405 | private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) { |
hannesw@415 | 406 | ensureObjectSetter(); |
jlaskey@3 | 407 | MethodHandle mh = createSetter(forType, type, primitiveSetter, objectSetter); |
jlaskey@3 | 408 | mh = debug(mh, currentType, type, "set"); |
jlaskey@3 | 409 | return mh; |
jlaskey@3 | 410 | } |
jlaskey@3 | 411 | |
jlaskey@3 | 412 | @Override |
jlaskey@3 | 413 | public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) { |
jlaskey@3 | 414 | final int i = getAccessorTypeIndex(type); |
jlaskey@3 | 415 | final int ci = currentType == null ? -1 : getAccessorTypeIndex(currentType); |
jlaskey@3 | 416 | final Class<?> forType = currentType == null ? type : currentType; |
jlaskey@3 | 417 | |
jlaskey@3 | 418 | //if we are asking for an object setter, but are still a primitive type, we might try to box it |
jlaskey@242 | 419 | MethodHandle mh; |
jlaskey@3 | 420 | |
jlaskey@3 | 421 | if (needsInvalidator(i, ci)) { |
jlaskey@3 | 422 | final Property newProperty = getWiderProperty(type); |
jlaskey@3 | 423 | final PropertyMap newMap = getWiderMap(currentMap, newProperty); |
jlaskey@3 | 424 | final MethodHandle widerSetter = newProperty.getSetter(type, newMap); |
jlaskey@3 | 425 | final MethodHandle explodeTypeSetter = MH.filterArguments(widerSetter, 0, MH.insertArguments(REPLACE_MAP, 1, newMap, getKey(), currentType, type)); |
jlaskey@3 | 426 | if (currentType != null && currentType.isPrimitive() && type == Object.class) { |
jlaskey@3 | 427 | //might try a box check on this to avoid widening field to object storage |
jlaskey@242 | 428 | mh = createGuardBoxedPrimitiveSetter(currentType, generateSetter(currentType, currentType), explodeTypeSetter); |
jlaskey@242 | 429 | } else { |
jlaskey@242 | 430 | mh = explodeTypeSetter; |
jlaskey@3 | 431 | } |
jlaskey@242 | 432 | } else { |
jlaskey@242 | 433 | mh = generateSetter(forType, type); |
jlaskey@3 | 434 | } |
jlaskey@3 | 435 | |
hannesw@291 | 436 | return mh; |
jlaskey@3 | 437 | } |
jlaskey@3 | 438 | |
jlaskey@3 | 439 | @Override |
jlaskey@3 | 440 | public boolean canChangeType() { |
jlaskey@3 | 441 | if (OBJECT_FIELDS_ONLY) { |
jlaskey@3 | 442 | return false; |
jlaskey@3 | 443 | } |
jlaskey@3 | 444 | return currentType != Object.class && (isConfigurable() || isWritable()); |
jlaskey@3 | 445 | } |
jlaskey@3 | 446 | |
jlaskey@3 | 447 | private boolean needsInvalidator(final int ti, final int fti) { |
jlaskey@3 | 448 | return canChangeType() && ti > fti; |
jlaskey@3 | 449 | } |
jlaskey@3 | 450 | |
jlaskey@3 | 451 | private void invalidate(final Class<?> newType) { |
jlaskey@3 | 452 | getters = new MethodHandle[NOOF_TYPES]; |
jlaskey@3 | 453 | setCurrentType(newType); |
jlaskey@3 | 454 | } |
jlaskey@3 | 455 | |
hannesw@291 | 456 | private MethodHandle getSpillGetter() { |
hannesw@291 | 457 | final int slot = getSlot(); |
hannesw@291 | 458 | MethodHandle getter = slot < SPILL_CACHE_SIZE ? SPILL_ACCESSORS[slot * 2] : null; |
hannesw@291 | 459 | if (getter == null) { |
jlaskey@410 | 460 | getter = MH.insertArguments(SPILL_ELEMENT_GETTER, 1, slot); |
hannesw@291 | 461 | if (slot < SPILL_CACHE_SIZE) { |
jlaskey@410 | 462 | SPILL_ACCESSORS[slot * 2 + 0] = getter; |
hannesw@291 | 463 | } |
hannesw@291 | 464 | } |
hannesw@291 | 465 | return getter; |
hannesw@291 | 466 | } |
hannesw@291 | 467 | |
hannesw@291 | 468 | private MethodHandle getSpillSetter() { |
hannesw@291 | 469 | final int slot = getSlot(); |
hannesw@291 | 470 | MethodHandle setter = slot < SPILL_CACHE_SIZE ? SPILL_ACCESSORS[slot * 2 + 1] : null; |
hannesw@291 | 471 | if (setter == null) { |
jlaskey@410 | 472 | setter = MH.insertArguments(SPILL_ELEMENT_SETTER, 1, slot); |
hannesw@291 | 473 | if (slot < SPILL_CACHE_SIZE) { |
hannesw@291 | 474 | SPILL_ACCESSORS[slot * 2 + 1] = setter; |
hannesw@291 | 475 | } |
hannesw@291 | 476 | } |
hannesw@291 | 477 | return setter; |
hannesw@291 | 478 | } |
hannesw@291 | 479 | |
jlaskey@3 | 480 | private static void finest(final String str) { |
jlaskey@3 | 481 | if (DEBUG_FIELDS) { |
jlaskey@3 | 482 | LOG.finest(str); |
jlaskey@3 | 483 | } |
jlaskey@3 | 484 | } |
jlaskey@3 | 485 | |
jlaskey@3 | 486 | private static void info(final String str) { |
jlaskey@3 | 487 | if (DEBUG_FIELDS) { |
jlaskey@3 | 488 | LOG.info(str); |
jlaskey@3 | 489 | } |
jlaskey@3 | 490 | } |
jlaskey@3 | 491 | |
jlaskey@3 | 492 | private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) { |
jlaskey@3 | 493 | if (DEBUG_FIELDS) { |
lagergren@137 | 494 | return MethodHandleFactory.addDebugPrintout( |
jlaskey@3 | 495 | LOG, |
jlaskey@3 | 496 | mh, |
jlaskey@3 | 497 | tag + " '" + getKey() + "' (property="+ Debug.id(this) + ", forType=" + stripName(forType) + ", type=" + stripName(type) + ')'); |
jlaskey@3 | 498 | } |
jlaskey@3 | 499 | return mh; |
jlaskey@3 | 500 | } |
jlaskey@3 | 501 | |
jlaskey@3 | 502 | private void setCurrentType(final Class<?> currentType) { |
jlaskey@3 | 503 | this.currentType = currentType; |
jlaskey@3 | 504 | } |
jlaskey@3 | 505 | |
jlaskey@3 | 506 | @Override |
jlaskey@3 | 507 | public Class<?> getCurrentType() { |
jlaskey@3 | 508 | return currentType; |
jlaskey@3 | 509 | } |
jlaskey@3 | 510 | |
jlaskey@3 | 511 | private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { |
attila@224 | 512 | return MH.findStatic(lookup, AccessorProperty.class, name, MH.type(rtype, types)); |
jlaskey@3 | 513 | } |
jlaskey@3 | 514 | |
jlaskey@3 | 515 | } |