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

Sat, 09 Mar 2013 21:49:32 +0530

author
sundar
date
Sat, 09 Mar 2013 21:49:32 +0530
changeset 133
5759f600fcf7
parent 131
fe5211fc3114
child 137
e15806b9d716
permissions
-rw-r--r--

8009559: clean up method handle lookup code.
Reviewed-by: ahgross, jlaskey, attila, 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 *
jlaskey@3 54 * @see SpillProperty
jlaskey@3 55 */
jlaskey@3 56 public class AccessorProperty extends Property {
jlaskey@3 57 private static final MethodHandle REPLACE_MAP = findOwnMH("replaceMap", Object.class, Object.class, PropertyMap.class, String.class, Class.class, Class.class);
jlaskey@3 58
jlaskey@3 59 private static final int NOOF_TYPES = getNumberOfAccessorTypes();
jlaskey@3 60
jlaskey@3 61 /** Property getter cache */
jlaskey@3 62 private MethodHandle[] getters = new MethodHandle[NOOF_TYPES];
jlaskey@3 63
jlaskey@3 64 private static final MethodType[] ACCESSOR_GETTER_TYPES = new MethodType[NOOF_TYPES];
jlaskey@3 65 private static final MethodType[] ACCESSOR_SETTER_TYPES = new MethodType[NOOF_TYPES];
jlaskey@3 66
jlaskey@3 67 /** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
jlaskey@3 68 private MethodHandle primitiveGetter;
jlaskey@3 69
jlaskey@3 70 /** Seed setter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
jlaskey@3 71 private MethodHandle primitiveSetter;
jlaskey@3 72
jlaskey@3 73 /** Seed getter for the Object version of this field */
jlaskey@3 74 private MethodHandle objectGetter;
jlaskey@3 75
jlaskey@3 76 /** Seed setter for the Object version of this field */
jlaskey@3 77 private MethodHandle objectSetter;
jlaskey@3 78
jlaskey@3 79 /**
jlaskey@3 80 * Current type of this object, in object only mode, this is an Object.class. In dual-fields mode
jlaskey@3 81 * null means undefined, and primitive types are allowed. The reason a special type is used for
jlaskey@3 82 * undefined, is that are no bits left to represent it in primitive types
jlaskey@3 83 */
jlaskey@3 84 private Class<?> currentType;
jlaskey@3 85
jlaskey@3 86 static {
jlaskey@3 87 for (int i = 0; i < NOOF_TYPES; i++) {
jlaskey@3 88 final Type type = ACCESSOR_TYPES.get(i);
jlaskey@3 89 ACCESSOR_GETTER_TYPES[i] = MH.type(type.getTypeClass(), Object.class);
jlaskey@3 90 ACCESSOR_SETTER_TYPES[i] = MH.type(void.class, Object.class, type.getTypeClass());
jlaskey@3 91 }
jlaskey@3 92 }
jlaskey@3 93
jlaskey@3 94 /**
jlaskey@3 95 * Delegate constructor. This is used when adding properties to the Global scope, which
jlaskey@3 96 * is necessary for outermost levels in a script (the ScriptObject is represented by
jlaskey@131 97 * a JO-prefixed ScriptObject class, but the properties need to be in the Global scope
jlaskey@3 98 * and are thus rebound with that as receiver
jlaskey@3 99 *
jlaskey@3 100 * @param property accessor property to rebind
jlaskey@3 101 * @param delegate delegate script object to rebind receiver to
jlaskey@3 102 */
jlaskey@3 103 public AccessorProperty(final AccessorProperty property, final ScriptObject delegate) {
jlaskey@3 104 this(property);
jlaskey@3 105
jlaskey@3 106 this.getters = new MethodHandle[NOOF_TYPES];
jlaskey@3 107
jlaskey@3 108 this.primitiveGetter = bindTo(primitiveGetter, delegate);
jlaskey@3 109 this.primitiveSetter = bindTo(primitiveSetter, delegate);
jlaskey@3 110 this.objectGetter = bindTo(objectGetter, delegate);
jlaskey@3 111 this.objectSetter = bindTo(objectSetter, delegate);
jlaskey@3 112
jlaskey@3 113 setCurrentType(property.getCurrentType());
jlaskey@3 114 }
jlaskey@3 115
jlaskey@3 116 /**
jlaskey@3 117 * Constructor. Similar to the constructor with both primitive getters and setters, the difference
jlaskey@3 118 * here being that only one getter and setter (setter is optional for non writable fields) is given
jlaskey@3 119 * to the constructor, and the rest are created from those. Used e.g. by Nasgen classes
jlaskey@3 120 *
jlaskey@3 121 * @param key the property key
jlaskey@3 122 * @param flags the property flags
jlaskey@80 123 * @param slot the property field number or spill slot
jlaskey@3 124 * @param getter the property getter
jlaskey@3 125 * @param setter the property setter or null if non writable, non configurable
jlaskey@3 126 */
jlaskey@80 127 public AccessorProperty(final String key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) {
jlaskey@80 128 super(key, flags, slot);
jlaskey@3 129
jlaskey@3 130 // we don't need to prep the setters these will never be invalidated as this is a nasgen
jlaskey@3 131 // or known type getter/setter. No invalidations will take place
jlaskey@3 132
jlaskey@3 133 final Class<?> getterType = getter.type().returnType();
jlaskey@3 134 final Class<?> setterType = setter == null ? null : setter.type().parameterType(1);
jlaskey@3 135
jlaskey@3 136 assert setterType == null || setterType == getterType;
jlaskey@3 137
jlaskey@3 138 if (getterType.isPrimitive()) {
jlaskey@3 139 for (int i = 0; i < NOOF_TYPES; i++) {
jlaskey@3 140 getters[i] = MH.asType(
jlaskey@3 141 Lookup.filterReturnType(
jlaskey@3 142 getter,
jlaskey@3 143 getAccessorType(i).getTypeClass()),
jlaskey@3 144 ACCESSOR_GETTER_TYPES[i]);
jlaskey@3 145 }
jlaskey@3 146 } else {
jlaskey@3 147 //this will work as the object setter and getter will be converted appropriately
jlaskey@3 148 objectGetter = getter;
jlaskey@3 149 objectSetter = setter;
jlaskey@3 150 }
jlaskey@3 151
jlaskey@3 152 setCurrentType(getterType);
jlaskey@3 153 }
jlaskey@3 154
jlaskey@3 155 /**
jlaskey@80 156 * Constructor for dual field AccessorPropertys.
jlaskey@80 157 *
jlaskey@80 158 * @param key property key
jlaskey@80 159 * @param flags property flags
jlaskey@80 160 * @param structure structure for objects associated with this property
jlaskey@80 161 * @param slot property field number or spill slot
jlaskey@80 162 */
jlaskey@80 163 public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot) {
jlaskey@80 164 super(key, flags, slot);
jlaskey@80 165
jlaskey@80 166 /*
jlaskey@80 167 *
jlaskey@80 168 * primitiveGetter and primitiveSetter are only used in dual fields mode. Setting them to null also
jlaskey@80 169 * works in dual field mode, it only means that the property never has a primitive
jlaskey@80 170 * representation.
jlaskey@80 171 */
jlaskey@80 172 primitiveGetter = null;
jlaskey@80 173 primitiveSetter = null;
jlaskey@80 174
jlaskey@80 175 final MethodHandles.Lookup lookup = MethodHandles.lookup();
jlaskey@80 176
jlaskey@80 177 if (isParameter() && hasArguments()) {
jlaskey@80 178 final MethodHandle arguments = MH.getter(MethodHandles.lookup(), structure, "arguments", Object.class);
jlaskey@80 179 final MethodHandle argumentsSO = MH.asType(arguments, arguments.type().changeReturnType(ScriptObject.class));
jlaskey@80 180
jlaskey@80 181 objectGetter = MH.insertArguments(MH.filterArguments(ScriptObject.GET_ARGUMENT.methodHandle(), 0, argumentsSO), 1, slot);
jlaskey@80 182 objectSetter = MH.insertArguments(MH.filterArguments(ScriptObject.SET_ARGUMENT.methodHandle(), 0, argumentsSO), 1, slot);
jlaskey@80 183 } else {
jlaskey@80 184 final String fieldNameObject = ObjectClassGenerator.getFieldName(slot, Type.OBJECT);
jlaskey@80 185 final String fieldNamePrimitive = ObjectClassGenerator.getFieldName(slot, ObjectClassGenerator.PRIMITIVE_TYPE);
jlaskey@80 186
jlaskey@80 187 objectGetter = MH.getter(lookup, structure, fieldNameObject, Type.OBJECT.getTypeClass());
jlaskey@80 188 objectSetter = MH.setter(lookup, structure, fieldNameObject, Type.OBJECT.getTypeClass());
jlaskey@80 189
jlaskey@80 190 if (!OBJECT_FIELDS_ONLY) {
jlaskey@80 191 primitiveGetter = MH.getter(lookup, structure, fieldNamePrimitive, PRIMITIVE_TYPE.getTypeClass());
jlaskey@80 192 primitiveSetter = MH.setter(lookup, structure, fieldNamePrimitive, PRIMITIVE_TYPE.getTypeClass());
jlaskey@80 193 }
jlaskey@80 194 }
jlaskey@80 195
jlaskey@80 196 Class<?> initialType = null;
jlaskey@80 197
jlaskey@80 198 if (OBJECT_FIELDS_ONLY || isAlwaysObject()) {
jlaskey@80 199 initialType = Object.class;
jlaskey@80 200 } else if (!canBePrimitive()) {
jlaskey@80 201 info(key + " cannot be primitive");
jlaskey@80 202 initialType = Object.class;
jlaskey@80 203 } else {
jlaskey@80 204 info(key + " CAN be primitive");
jlaskey@80 205 if (!canBeUndefined()) {
jlaskey@80 206 info(key + " is always defined");
jlaskey@80 207 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 208 }
jlaskey@80 209 }
jlaskey@80 210
jlaskey@80 211 // is always object means "is never initialized to undefined, and always of object type
jlaskey@80 212 setCurrentType(initialType);
jlaskey@80 213 }
jlaskey@80 214
jlaskey@80 215 /**
jlaskey@3 216 * Copy constructor
jlaskey@3 217 *
jlaskey@3 218 * @param property source property
jlaskey@3 219 */
jlaskey@3 220 protected AccessorProperty(final AccessorProperty property) {
jlaskey@3 221 super(property);
jlaskey@3 222
jlaskey@3 223 this.getters = property.getters;
jlaskey@3 224 this.primitiveGetter = property.primitiveGetter;
jlaskey@3 225 this.primitiveSetter = property.primitiveSetter;
jlaskey@3 226 this.objectGetter = property.objectGetter;
jlaskey@3 227 this.objectSetter = property.objectSetter;
jlaskey@3 228
jlaskey@3 229 setCurrentType(property.getCurrentType());
jlaskey@3 230 }
jlaskey@3 231
jlaskey@3 232 private static MethodHandle bindTo(final MethodHandle mh, final Object receiver) {
jlaskey@3 233 if (mh == null) {
jlaskey@3 234 return null;
jlaskey@3 235 }
jlaskey@3 236
jlaskey@3 237 return MH.dropArguments(MH.bindTo(mh, receiver), 0, Object.class);
jlaskey@3 238 }
jlaskey@3 239
jlaskey@3 240 @Override
jlaskey@3 241 protected Property copy() {
jlaskey@3 242 return new AccessorProperty(this);
jlaskey@3 243 }
jlaskey@3 244
jlaskey@3 245 @Override
jlaskey@3 246 public MethodHandle getGetter(final Class<?> type) {
jlaskey@3 247 final int i = getAccessorTypeIndex(type);
jlaskey@3 248 if (getters[i] == null) {
jlaskey@3 249 getters[i] = debug(
jlaskey@3 250 MH.asType(
jlaskey@3 251 createGetter(
jlaskey@3 252 currentType,
jlaskey@3 253 type,
jlaskey@3 254 primitiveGetter,
jlaskey@3 255 objectGetter),
jlaskey@3 256 ACCESSOR_GETTER_TYPES[i]),
jlaskey@3 257 currentType,
jlaskey@3 258 type,
jlaskey@3 259 "get");
jlaskey@3 260 }
jlaskey@3 261
jlaskey@3 262 return getters[i];
jlaskey@3 263 }
jlaskey@3 264
jlaskey@3 265 private Property getWiderProperty(final Class<?> type) {
jlaskey@3 266 final AccessorProperty newProperty = new AccessorProperty(this);
jlaskey@3 267 newProperty.invalidate(type);
jlaskey@3 268 return newProperty;
jlaskey@3 269 }
jlaskey@3 270
jlaskey@3 271 private PropertyMap getWiderMap(final PropertyMap oldMap, final Property newProperty) {
jlaskey@3 272 final PropertyMap newMap = oldMap.replaceProperty(this, newProperty);
jlaskey@3 273 assert oldMap.size() > 0;
jlaskey@3 274 assert newMap.size() == oldMap.size();
jlaskey@3 275 return newMap;
jlaskey@3 276 }
jlaskey@3 277
jlaskey@3 278 // the final three arguments are for debug printout purposes only
jlaskey@3 279 @SuppressWarnings("unused")
jlaskey@3 280 private static Object replaceMap(final Object sobj, final PropertyMap newMap, final String key, final Class<?> oldType, final Class<?> newType) {
jlaskey@3 281 if (DEBUG_FIELDS) {
jlaskey@3 282 final PropertyMap oldMap = ((ScriptObject)sobj).getMap();
jlaskey@3 283 info("Type change for '" + key + "' " + oldType + "=>" + newType);
jlaskey@3 284 finest("setting map " + sobj + " from " + Debug.id(oldMap) + " to " + Debug.id(newMap) + " " + oldMap + " => " + newMap);
jlaskey@3 285 }
jlaskey@3 286 ((ScriptObject)sobj).setMap(newMap);
jlaskey@3 287 return sobj;
jlaskey@3 288 }
jlaskey@3 289
jlaskey@3 290 private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) {
jlaskey@3 291 MethodHandle mh = createSetter(forType, type, primitiveSetter, objectSetter);
jlaskey@3 292 mh = MH.asType(mh, ACCESSOR_SETTER_TYPES[getAccessorTypeIndex(type)]); //has to be the case for invokeexact to work in ScriptObject
jlaskey@3 293 mh = debug(mh, currentType, type, "set");
jlaskey@3 294 return mh;
jlaskey@3 295 }
jlaskey@3 296
jlaskey@3 297 @Override
jlaskey@3 298 public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) {
jlaskey@3 299 final int i = getAccessorTypeIndex(type);
jlaskey@3 300 final int ci = currentType == null ? -1 : getAccessorTypeIndex(currentType);
jlaskey@3 301 final Class<?> forType = currentType == null ? type : currentType;
jlaskey@3 302
jlaskey@3 303 //if we are asking for an object setter, but are still a primitive type, we might try to box it
jlaskey@3 304
jlaskey@3 305 if (needsInvalidator(i, ci)) {
jlaskey@3 306 final Property newProperty = getWiderProperty(type);
jlaskey@3 307 final PropertyMap newMap = getWiderMap(currentMap, newProperty);
jlaskey@3 308 final MethodHandle widerSetter = newProperty.getSetter(type, newMap);
jlaskey@3 309 final MethodHandle explodeTypeSetter = MH.filterArguments(widerSetter, 0, MH.insertArguments(REPLACE_MAP, 1, newMap, getKey(), currentType, type));
jlaskey@3 310 if (currentType != null && currentType.isPrimitive() && type == Object.class) {
jlaskey@3 311 //might try a box check on this to avoid widening field to object storage
jlaskey@3 312 return createGuardBoxedPrimitiveSetter(currentType, generateSetter(currentType, currentType), explodeTypeSetter);
jlaskey@3 313 }
jlaskey@3 314 return explodeTypeSetter;
jlaskey@3 315 }
jlaskey@3 316
jlaskey@3 317 return generateSetter(forType, type);
jlaskey@3 318 }
jlaskey@3 319
jlaskey@3 320 @Override
jlaskey@3 321 public boolean canChangeType() {
jlaskey@3 322 if (OBJECT_FIELDS_ONLY) {
jlaskey@3 323 return false;
jlaskey@3 324 }
jlaskey@3 325 return currentType != Object.class && (isConfigurable() || isWritable());
jlaskey@3 326 }
jlaskey@3 327
jlaskey@3 328 private boolean needsInvalidator(final int ti, final int fti) {
jlaskey@3 329 return canChangeType() && ti > fti;
jlaskey@3 330 }
jlaskey@3 331
jlaskey@3 332 private void invalidate(final Class<?> newType) {
jlaskey@3 333 getters = new MethodHandle[NOOF_TYPES];
jlaskey@3 334 setCurrentType(newType);
jlaskey@3 335 }
jlaskey@3 336
jlaskey@3 337 private static void finest(final String str) {
jlaskey@3 338 if (DEBUG_FIELDS) {
jlaskey@3 339 LOG.finest(str);
jlaskey@3 340 }
jlaskey@3 341 }
jlaskey@3 342
jlaskey@3 343 private static void info(final String str) {
jlaskey@3 344 if (DEBUG_FIELDS) {
jlaskey@3 345 LOG.info(str);
jlaskey@3 346 }
jlaskey@3 347 }
jlaskey@3 348
jlaskey@3 349 private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) {
jlaskey@3 350 if (DEBUG_FIELDS) {
jlaskey@3 351 final MethodHandle mhd = MethodHandleFactory.addDebugPrintout(
jlaskey@3 352 LOG,
jlaskey@3 353 mh,
jlaskey@3 354 tag + " '" + getKey() + "' (property="+ Debug.id(this) + ", forType=" + stripName(forType) + ", type=" + stripName(type) + ')');
jlaskey@3 355 return mhd;
jlaskey@3 356 }
jlaskey@3 357 return mh;
jlaskey@3 358 }
jlaskey@3 359
jlaskey@3 360 private void setCurrentType(final Class<?> currentType) {
jlaskey@3 361 this.currentType = currentType;
jlaskey@3 362 }
jlaskey@3 363
jlaskey@3 364 @Override
jlaskey@3 365 public Class<?> getCurrentType() {
jlaskey@3 366 return currentType;
jlaskey@3 367 }
jlaskey@3 368
jlaskey@3 369 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
jlaskey@3 370 return MH.findStatic(MethodHandles.lookup(), AccessorProperty.class, name, MH.type(rtype, types));
jlaskey@3 371 }
jlaskey@3 372
jlaskey@3 373 }

mercurial