src/jdk/nashorn/internal/runtime/AccessorProperty.java

Wed, 12 Mar 2014 11:26:00 +0100

author
hannesw
date
Wed, 12 Mar 2014 11:26:00 +0100
changeset 769
5a1ae83c295f
parent 476
fbd21b00197b
child 828
e0e2d72e6699
permissions
-rw-r--r--

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 }

mercurial