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

Tue, 12 Mar 2013 15:30:53 +0100

author
lagergren
date
Tue, 12 Mar 2013 15:30:53 +0100
changeset 137
e15806b9d716
parent 133
5759f600fcf7
child 224
ff1e4655a57f
permissions
-rw-r--r--

8009718: Lazy execution architecture continued - ScriptFunctionData is either final or recompilable. Moved ScriptFunctionData creation logic away from runtime to compile time. Prepared for method generation/specialization. Got rid of ScriptFunctionImplTrampoline whose semantics could be done as part of the relinking anyway. Merge with the lookup package change.
Reviewed-by: attila, jlaskey

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 * primitiveGetter and primitiveSetter are only used in dual fields mode. Setting them to null also
jlaskey@80 168 * works in dual field mode, it only means that the property never has a primitive
jlaskey@80 169 * representation.
jlaskey@80 170 */
jlaskey@80 171 primitiveGetter = null;
jlaskey@80 172 primitiveSetter = null;
jlaskey@80 173
jlaskey@80 174 final MethodHandles.Lookup lookup = MethodHandles.lookup();
jlaskey@80 175
jlaskey@80 176 if (isParameter() && hasArguments()) {
jlaskey@80 177 final MethodHandle arguments = MH.getter(MethodHandles.lookup(), structure, "arguments", Object.class);
jlaskey@80 178 final MethodHandle argumentsSO = MH.asType(arguments, arguments.type().changeReturnType(ScriptObject.class));
jlaskey@80 179
jlaskey@80 180 objectGetter = MH.insertArguments(MH.filterArguments(ScriptObject.GET_ARGUMENT.methodHandle(), 0, argumentsSO), 1, slot);
jlaskey@80 181 objectSetter = MH.insertArguments(MH.filterArguments(ScriptObject.SET_ARGUMENT.methodHandle(), 0, argumentsSO), 1, slot);
jlaskey@80 182 } else {
jlaskey@80 183 final String fieldNameObject = ObjectClassGenerator.getFieldName(slot, Type.OBJECT);
jlaskey@80 184 final String fieldNamePrimitive = ObjectClassGenerator.getFieldName(slot, ObjectClassGenerator.PRIMITIVE_TYPE);
jlaskey@80 185
jlaskey@80 186 objectGetter = MH.getter(lookup, structure, fieldNameObject, Type.OBJECT.getTypeClass());
jlaskey@80 187 objectSetter = MH.setter(lookup, structure, fieldNameObject, Type.OBJECT.getTypeClass());
jlaskey@80 188
jlaskey@80 189 if (!OBJECT_FIELDS_ONLY) {
jlaskey@80 190 primitiveGetter = MH.getter(lookup, structure, fieldNamePrimitive, PRIMITIVE_TYPE.getTypeClass());
jlaskey@80 191 primitiveSetter = MH.setter(lookup, structure, fieldNamePrimitive, PRIMITIVE_TYPE.getTypeClass());
jlaskey@80 192 }
jlaskey@80 193 }
jlaskey@80 194
jlaskey@80 195 Class<?> initialType = null;
jlaskey@80 196
jlaskey@80 197 if (OBJECT_FIELDS_ONLY || isAlwaysObject()) {
jlaskey@80 198 initialType = Object.class;
jlaskey@80 199 } else if (!canBePrimitive()) {
jlaskey@80 200 info(key + " cannot be primitive");
jlaskey@80 201 initialType = Object.class;
jlaskey@80 202 } else {
jlaskey@80 203 info(key + " CAN be primitive");
jlaskey@80 204 if (!canBeUndefined()) {
jlaskey@80 205 info(key + " is always defined");
jlaskey@80 206 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 207 }
jlaskey@80 208 }
jlaskey@80 209
jlaskey@80 210 // is always object means "is never initialized to undefined, and always of object type
jlaskey@80 211 setCurrentType(initialType);
jlaskey@80 212 }
jlaskey@80 213
jlaskey@80 214 /**
jlaskey@3 215 * Copy constructor
jlaskey@3 216 *
jlaskey@3 217 * @param property source property
jlaskey@3 218 */
jlaskey@3 219 protected AccessorProperty(final AccessorProperty property) {
jlaskey@3 220 super(property);
jlaskey@3 221
jlaskey@3 222 this.getters = property.getters;
jlaskey@3 223 this.primitiveGetter = property.primitiveGetter;
jlaskey@3 224 this.primitiveSetter = property.primitiveSetter;
jlaskey@3 225 this.objectGetter = property.objectGetter;
jlaskey@3 226 this.objectSetter = property.objectSetter;
jlaskey@3 227
jlaskey@3 228 setCurrentType(property.getCurrentType());
jlaskey@3 229 }
jlaskey@3 230
jlaskey@3 231 private static MethodHandle bindTo(final MethodHandle mh, final Object receiver) {
jlaskey@3 232 if (mh == null) {
jlaskey@3 233 return null;
jlaskey@3 234 }
jlaskey@3 235
jlaskey@3 236 return MH.dropArguments(MH.bindTo(mh, receiver), 0, Object.class);
jlaskey@3 237 }
jlaskey@3 238
jlaskey@3 239 @Override
jlaskey@3 240 protected Property copy() {
jlaskey@3 241 return new AccessorProperty(this);
jlaskey@3 242 }
jlaskey@3 243
jlaskey@3 244 @Override
jlaskey@3 245 public MethodHandle getGetter(final Class<?> type) {
jlaskey@3 246 final int i = getAccessorTypeIndex(type);
jlaskey@3 247 if (getters[i] == null) {
jlaskey@3 248 getters[i] = debug(
jlaskey@3 249 MH.asType(
jlaskey@3 250 createGetter(
jlaskey@3 251 currentType,
jlaskey@3 252 type,
jlaskey@3 253 primitiveGetter,
jlaskey@3 254 objectGetter),
jlaskey@3 255 ACCESSOR_GETTER_TYPES[i]),
jlaskey@3 256 currentType,
jlaskey@3 257 type,
jlaskey@3 258 "get");
jlaskey@3 259 }
jlaskey@3 260
jlaskey@3 261 return getters[i];
jlaskey@3 262 }
jlaskey@3 263
jlaskey@3 264 private Property getWiderProperty(final Class<?> type) {
jlaskey@3 265 final AccessorProperty newProperty = new AccessorProperty(this);
jlaskey@3 266 newProperty.invalidate(type);
jlaskey@3 267 return newProperty;
jlaskey@3 268 }
jlaskey@3 269
jlaskey@3 270 private PropertyMap getWiderMap(final PropertyMap oldMap, final Property newProperty) {
jlaskey@3 271 final PropertyMap newMap = oldMap.replaceProperty(this, newProperty);
jlaskey@3 272 assert oldMap.size() > 0;
jlaskey@3 273 assert newMap.size() == oldMap.size();
jlaskey@3 274 return newMap;
jlaskey@3 275 }
jlaskey@3 276
jlaskey@3 277 // the final three arguments are for debug printout purposes only
jlaskey@3 278 @SuppressWarnings("unused")
jlaskey@3 279 private static Object replaceMap(final Object sobj, final PropertyMap newMap, final String key, final Class<?> oldType, final Class<?> newType) {
jlaskey@3 280 if (DEBUG_FIELDS) {
jlaskey@3 281 final PropertyMap oldMap = ((ScriptObject)sobj).getMap();
jlaskey@3 282 info("Type change for '" + key + "' " + oldType + "=>" + newType);
jlaskey@3 283 finest("setting map " + sobj + " from " + Debug.id(oldMap) + " to " + Debug.id(newMap) + " " + oldMap + " => " + newMap);
jlaskey@3 284 }
jlaskey@3 285 ((ScriptObject)sobj).setMap(newMap);
jlaskey@3 286 return sobj;
jlaskey@3 287 }
jlaskey@3 288
jlaskey@3 289 private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) {
jlaskey@3 290 MethodHandle mh = createSetter(forType, type, primitiveSetter, objectSetter);
jlaskey@3 291 mh = MH.asType(mh, ACCESSOR_SETTER_TYPES[getAccessorTypeIndex(type)]); //has to be the case for invokeexact to work in ScriptObject
jlaskey@3 292 mh = debug(mh, currentType, type, "set");
jlaskey@3 293 return mh;
jlaskey@3 294 }
jlaskey@3 295
jlaskey@3 296 @Override
jlaskey@3 297 public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) {
jlaskey@3 298 final int i = getAccessorTypeIndex(type);
jlaskey@3 299 final int ci = currentType == null ? -1 : getAccessorTypeIndex(currentType);
jlaskey@3 300 final Class<?> forType = currentType == null ? type : currentType;
jlaskey@3 301
jlaskey@3 302 //if we are asking for an object setter, but are still a primitive type, we might try to box it
jlaskey@3 303
jlaskey@3 304 if (needsInvalidator(i, ci)) {
jlaskey@3 305 final Property newProperty = getWiderProperty(type);
jlaskey@3 306 final PropertyMap newMap = getWiderMap(currentMap, newProperty);
jlaskey@3 307 final MethodHandle widerSetter = newProperty.getSetter(type, newMap);
jlaskey@3 308 final MethodHandle explodeTypeSetter = MH.filterArguments(widerSetter, 0, MH.insertArguments(REPLACE_MAP, 1, newMap, getKey(), currentType, type));
jlaskey@3 309 if (currentType != null && currentType.isPrimitive() && type == Object.class) {
jlaskey@3 310 //might try a box check on this to avoid widening field to object storage
jlaskey@3 311 return createGuardBoxedPrimitiveSetter(currentType, generateSetter(currentType, currentType), explodeTypeSetter);
jlaskey@3 312 }
jlaskey@3 313 return explodeTypeSetter;
jlaskey@3 314 }
jlaskey@3 315
jlaskey@3 316 return generateSetter(forType, type);
jlaskey@3 317 }
jlaskey@3 318
jlaskey@3 319 @Override
jlaskey@3 320 public boolean canChangeType() {
jlaskey@3 321 if (OBJECT_FIELDS_ONLY) {
jlaskey@3 322 return false;
jlaskey@3 323 }
jlaskey@3 324 return currentType != Object.class && (isConfigurable() || isWritable());
jlaskey@3 325 }
jlaskey@3 326
jlaskey@3 327 private boolean needsInvalidator(final int ti, final int fti) {
jlaskey@3 328 return canChangeType() && ti > fti;
jlaskey@3 329 }
jlaskey@3 330
jlaskey@3 331 private void invalidate(final Class<?> newType) {
jlaskey@3 332 getters = new MethodHandle[NOOF_TYPES];
jlaskey@3 333 setCurrentType(newType);
jlaskey@3 334 }
jlaskey@3 335
jlaskey@3 336 private static void finest(final String str) {
jlaskey@3 337 if (DEBUG_FIELDS) {
jlaskey@3 338 LOG.finest(str);
jlaskey@3 339 }
jlaskey@3 340 }
jlaskey@3 341
jlaskey@3 342 private static void info(final String str) {
jlaskey@3 343 if (DEBUG_FIELDS) {
jlaskey@3 344 LOG.info(str);
jlaskey@3 345 }
jlaskey@3 346 }
jlaskey@3 347
jlaskey@3 348 private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) {
jlaskey@3 349 if (DEBUG_FIELDS) {
lagergren@137 350 return MethodHandleFactory.addDebugPrintout(
jlaskey@3 351 LOG,
jlaskey@3 352 mh,
jlaskey@3 353 tag + " '" + getKey() + "' (property="+ Debug.id(this) + ", forType=" + stripName(forType) + ", type=" + stripName(type) + ')');
jlaskey@3 354 }
jlaskey@3 355 return mh;
jlaskey@3 356 }
jlaskey@3 357
jlaskey@3 358 private void setCurrentType(final Class<?> currentType) {
jlaskey@3 359 this.currentType = currentType;
jlaskey@3 360 }
jlaskey@3 361
jlaskey@3 362 @Override
jlaskey@3 363 public Class<?> getCurrentType() {
jlaskey@3 364 return currentType;
jlaskey@3 365 }
jlaskey@3 366
jlaskey@3 367 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
jlaskey@3 368 return MH.findStatic(MethodHandles.lookup(), AccessorProperty.class, name, MH.type(rtype, types));
jlaskey@3 369 }
jlaskey@3 370
jlaskey@3 371 }

mercurial