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

Thu, 04 Sep 2014 18:47:18 +0200

author
hannesw
date
Thu, 04 Sep 2014 18:47:18 +0200
changeset 991
b7a2db4de254
parent 963
e2497b11a021
child 1012
bac02d5a397f
permissions
-rw-r--r--

8051889: Implement block scoping in symbol assignment and scope computation
Reviewed-by: attila, lagergren

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.OBJECT_FIELDS_ONLY;
attila@963 29 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE;
lagergren@96 30 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createGetter;
lagergren@96 31 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createSetter;
attila@963 32 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getFieldCount;
attila@963 33 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getFieldName;
sundar@133 34 import static jdk.nashorn.internal.lookup.Lookup.MH;
sundar@133 35 import static jdk.nashorn.internal.lookup.MethodHandleFactory.stripName;
attila@963 36 import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
attila@963 37 import static jdk.nashorn.internal.runtime.JSType.getNumberOfAccessorTypes;
attila@963 38 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
jlaskey@3 39
hannesw@828 40 import java.io.IOException;
hannesw@828 41 import java.io.ObjectInputStream;
jlaskey@3 42 import java.lang.invoke.MethodHandle;
jlaskey@3 43 import java.lang.invoke.MethodHandles;
attila@963 44 import java.lang.invoke.SwitchPoint;
attila@963 45 import java.util.function.Supplier;
attila@963 46 import java.util.logging.Level;
lagergren@96 47 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
jlaskey@3 48 import jdk.nashorn.internal.codegen.types.Type;
sundar@133 49 import jdk.nashorn.internal.lookup.Lookup;
attila@963 50 import jdk.nashorn.internal.objects.Global;
jlaskey@3 51
jlaskey@3 52 /**
jlaskey@3 53 * An AccessorProperty is the most generic property type. An AccessorProperty is
jlaskey@3 54 * represented as fields in a ScriptObject class.
jlaskey@3 55 */
attila@963 56 public class AccessorProperty extends Property {
attila@963 57 private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
attila@963 58
attila@963 59 private static final MethodHandle REPLACE_MAP = findOwnMH_S("replaceMap", Object.class, Object.class, PropertyMap.class);
attila@963 60 private static final MethodHandle INVALIDATE_SP = findOwnMH_S("invalidateSwitchPoint", Object.class, Object.class, SwitchPoint.class);
attila@963 61
attila@963 62 private static final SwitchPoint NO_CHANGE_CALLBACK = new SwitchPoint();
jlaskey@3 63
jlaskey@3 64 private static final int NOOF_TYPES = getNumberOfAccessorTypes();
hannesw@828 65 private static final long serialVersionUID = 3371720170182154920L;
jlaskey@3 66
attila@224 67 /**
attila@224 68 * Properties in different maps for the same structure class will share their field getters and setters. This could
attila@224 69 * be further extended to other method handles that are looked up in the AccessorProperty constructor, but right now
attila@224 70 * these are the most frequently retrieved ones, and lookup of method handle natives only registers in the profiler
attila@224 71 * for them.
attila@224 72 */
attila@963 73 private static ClassValue<Accessors> GETTERS_SETTERS = new ClassValue<Accessors>() {
attila@224 74 @Override
attila@963 75 protected Accessors computeValue(final Class<?> structure) {
attila@963 76 return new Accessors(structure);
attila@224 77 }
attila@224 78 };
attila@224 79
attila@963 80 private static class Accessors {
attila@963 81 final MethodHandle[] objectGetters;
attila@963 82 final MethodHandle[] objectSetters;
attila@963 83 final MethodHandle[] primitiveGetters;
attila@963 84 final MethodHandle[] primitiveSetters;
jlaskey@3 85
attila@963 86 /**
attila@963 87 * Normal
attila@963 88 * @param structure
attila@963 89 */
attila@963 90 Accessors(final Class<?> structure) {
attila@963 91 final int fieldCount = getFieldCount(structure);
attila@963 92 objectGetters = new MethodHandle[fieldCount];
attila@963 93 objectSetters = new MethodHandle[fieldCount];
attila@963 94 primitiveGetters = new MethodHandle[fieldCount];
attila@963 95 primitiveSetters = new MethodHandle[fieldCount];
hannesw@291 96
attila@963 97 for (int i = 0; i < fieldCount; i++) {
attila@963 98 final String fieldName = getFieldName(i, Type.OBJECT);
attila@963 99 final Class<?> typeClass = Type.OBJECT.getTypeClass();
attila@963 100 objectGetters[i] = MH.asType(MH.getter(LOOKUP, structure, fieldName, typeClass), Lookup.GET_OBJECT_TYPE);
attila@963 101 objectSetters[i] = MH.asType(MH.setter(LOOKUP, structure, fieldName, typeClass), Lookup.SET_OBJECT_TYPE);
attila@963 102 }
hannesw@291 103
attila@963 104 if (!OBJECT_FIELDS_ONLY) {
attila@963 105 for (int i = 0; i < fieldCount; i++) {
attila@963 106 final String fieldNamePrimitive = getFieldName(i, PRIMITIVE_FIELD_TYPE);
attila@963 107 final Class<?> typeClass = PRIMITIVE_FIELD_TYPE.getTypeClass();
attila@963 108 primitiveGetters[i] = MH.asType(MH.getter(LOOKUP, structure, fieldNamePrimitive, typeClass), Lookup.GET_PRIMITIVE_TYPE);
attila@963 109 primitiveSetters[i] = MH.asType(MH.setter(LOOKUP, structure, fieldNamePrimitive, typeClass), Lookup.SET_PRIMITIVE_TYPE);
attila@963 110 }
jlaskey@410 111 }
hannesw@291 112 }
attila@963 113 }
hannesw@291 114
attila@963 115 /**
attila@963 116 * Property getter cache
attila@963 117 * Note that we can't do the same simple caching for optimistic getters,
attila@963 118 * due to the fact that they are bound to a program point, which will
attila@963 119 * produce different boun method handles wrapping the same access mechanism
attila@963 120 * depending on callsite
attila@963 121 */
attila@963 122 private MethodHandle[] GETTER_CACHE = new MethodHandle[NOOF_TYPES];
jlaskey@3 123
sundar@418 124 /**
sundar@418 125 * Create a new accessor property. Factory method used by nasgen generated code.
sundar@418 126 *
sundar@418 127 * @param key {@link Property} key.
sundar@418 128 * @param propertyFlags {@link Property} flags.
sundar@418 129 * @param getter {@link Property} get accessor method.
sundar@418 130 * @param setter {@link Property} set accessor method.
sundar@418 131 *
sundar@418 132 * @return New {@link AccessorProperty} created.
sundar@418 133 */
sundar@418 134 public static AccessorProperty create(final String key, final int propertyFlags, final MethodHandle getter, final MethodHandle setter) {
sundar@418 135 return new AccessorProperty(key, propertyFlags, -1, getter, setter);
sundar@418 136 }
sundar@418 137
jlaskey@3 138 /** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
attila@963 139 transient MethodHandle primitiveGetter;
jlaskey@3 140
jlaskey@3 141 /** Seed setter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
attila@963 142 transient MethodHandle primitiveSetter;
jlaskey@3 143
jlaskey@3 144 /** Seed getter for the Object version of this field */
attila@963 145 transient MethodHandle objectGetter;
jlaskey@3 146
jlaskey@3 147 /** Seed setter for the Object version of this field */
attila@963 148 transient MethodHandle objectSetter;
jlaskey@3 149
jlaskey@3 150 /**
jlaskey@3 151 * Current type of this object, in object only mode, this is an Object.class. In dual-fields mode
jlaskey@3 152 * null means undefined, and primitive types are allowed. The reason a special type is used for
jlaskey@3 153 * undefined, is that are no bits left to represent it in primitive types
jlaskey@3 154 */
jlaskey@3 155 private Class<?> currentType;
jlaskey@3 156
jlaskey@3 157 /**
hannesw@769 158 * Delegate constructor for bound properties. This is used for properties created by
hannesw@769 159 * {@link ScriptRuntime#mergeScope} and the Nashorn {@code Object.bindProperties} method.
hannesw@769 160 * The former is used to add a script's defined globals to the current global scope while
hannesw@769 161 * still storing them in a JO-prefixed ScriptObject class.
hannesw@769 162 *
hannesw@769 163 * <p>All properties created by this constructor have the {@link #IS_BOUND} flag set.</p>
jlaskey@3 164 *
jlaskey@3 165 * @param property accessor property to rebind
sundar@423 166 * @param delegate delegate object to rebind receiver to
jlaskey@3 167 */
sundar@476 168 AccessorProperty(final AccessorProperty property, final Object delegate) {
attila@963 169 super(property, property.getFlags() | IS_BOUND);
jlaskey@3 170
hannesw@291 171 this.primitiveGetter = bindTo(property.primitiveGetter, delegate);
hannesw@291 172 this.primitiveSetter = bindTo(property.primitiveSetter, delegate);
attila@963 173 this.objectGetter = bindTo(property.objectGetter, delegate);
attila@963 174 this.objectSetter = bindTo(property.objectSetter, delegate);
attila@963 175 property.GETTER_CACHE = new MethodHandle[NOOF_TYPES];
hannesw@769 176 // Properties created this way are bound to a delegate
jlaskey@3 177 setCurrentType(property.getCurrentType());
jlaskey@3 178 }
jlaskey@3 179
jlaskey@3 180 /**
attila@963 181 * SPILL PROPERTY or USER ACCESSOR PROPERTY abstract constructor
attila@963 182 *
hannesw@291 183 * Constructor for spill properties. Array getters and setters will be created on demand.
hannesw@291 184 *
hannesw@291 185 * @param key the property key
hannesw@291 186 * @param flags the property flags
hannesw@291 187 * @param slot spill slot
attila@963 188 * @param primitiveGetter primitive getter
attila@963 189 * @param primitiveSetter primitive setter
attila@963 190 * @param objectGetter object getter
attila@963 191 * @param objectSetter object setter
hannesw@291 192 */
attila@963 193 protected AccessorProperty(
attila@963 194 final String key,
attila@963 195 final int flags,
attila@963 196 final int slot,
attila@963 197 final MethodHandle primitiveGetter,
attila@963 198 final MethodHandle primitiveSetter,
attila@963 199 final MethodHandle objectGetter,
attila@963 200 final MethodHandle objectSetter) {
hannesw@291 201 super(key, flags, slot);
attila@963 202 assert getClass() != AccessorProperty.class;
attila@963 203 this.primitiveGetter = primitiveGetter;
attila@963 204 this.primitiveSetter = primitiveSetter;
attila@963 205 this.objectGetter = objectGetter;
attila@963 206 this.objectSetter = objectSetter;
attila@963 207 initializeType();
hannesw@291 208 }
hannesw@291 209
hannesw@291 210 /**
attila@963 211 * NASGEN constructor
attila@963 212 *
jlaskey@3 213 * Constructor. Similar to the constructor with both primitive getters and setters, the difference
jlaskey@3 214 * here being that only one getter and setter (setter is optional for non writable fields) is given
jlaskey@3 215 * to the constructor, and the rest are created from those. Used e.g. by Nasgen classes
jlaskey@3 216 *
jlaskey@3 217 * @param key the property key
jlaskey@3 218 * @param flags the property flags
jlaskey@80 219 * @param slot the property field number or spill slot
jlaskey@3 220 * @param getter the property getter
jlaskey@3 221 * @param setter the property setter or null if non writable, non configurable
jlaskey@3 222 */
attila@963 223 private AccessorProperty(final String key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) {
attila@963 224 super(key, flags | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot);
attila@963 225 assert !isSpill();
jlaskey@3 226
jlaskey@3 227 // we don't need to prep the setters these will never be invalidated as this is a nasgen
jlaskey@3 228 // or known type getter/setter. No invalidations will take place
jlaskey@3 229
jlaskey@3 230 final Class<?> getterType = getter.type().returnType();
jlaskey@3 231 final Class<?> setterType = setter == null ? null : setter.type().parameterType(1);
jlaskey@3 232
jlaskey@3 233 assert setterType == null || setterType == getterType;
attila@963 234 if (OBJECT_FIELDS_ONLY) {
attila@963 235 primitiveGetter = primitiveSetter = null;
attila@963 236 } else {
attila@963 237 if (getterType == int.class || getterType == long.class) {
attila@963 238 primitiveGetter = MH.asType(getter, Lookup.GET_PRIMITIVE_TYPE);
attila@963 239 primitiveSetter = setter == null ? null : MH.asType(setter, Lookup.SET_PRIMITIVE_TYPE);
attila@963 240 } else if (getterType == double.class) {
attila@963 241 primitiveGetter = MH.asType(MH.filterReturnValue(getter, ObjectClassGenerator.PACK_DOUBLE), Lookup.GET_PRIMITIVE_TYPE);
attila@963 242 primitiveSetter = setter == null ? null : MH.asType(MH.filterArguments(setter, 1, ObjectClassGenerator.UNPACK_DOUBLE), Lookup.SET_PRIMITIVE_TYPE);
attila@963 243 } else {
attila@963 244 primitiveGetter = primitiveSetter = null;
jlaskey@3 245 }
jlaskey@3 246 }
jlaskey@3 247
attila@963 248 assert primitiveGetter == null || primitiveGetter.type() == Lookup.GET_PRIMITIVE_TYPE : primitiveGetter + "!=" + Lookup.GET_PRIMITIVE_TYPE;
attila@963 249 assert primitiveSetter == null || primitiveSetter.type() == Lookup.SET_PRIMITIVE_TYPE : primitiveSetter;
jlaskey@3 250
attila@963 251 objectGetter = getter.type() != Lookup.GET_OBJECT_TYPE ? MH.asType(getter, Lookup.GET_OBJECT_TYPE) : getter;
attila@963 252 objectSetter = setter != null && setter.type() != Lookup.SET_OBJECT_TYPE ? MH.asType(setter, Lookup.SET_OBJECT_TYPE) : setter;
attila@224 253
attila@963 254 setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : getterType);
attila@224 255 }
attila@224 256
jlaskey@3 257 /**
attila@963 258 * Normal ACCESS PROPERTY constructor given a structure class.
jlaskey@80 259 * Constructor for dual field AccessorPropertys.
jlaskey@80 260 *
jlaskey@80 261 * @param key property key
jlaskey@80 262 * @param flags property flags
jlaskey@80 263 * @param structure structure for objects associated with this property
jlaskey@80 264 * @param slot property field number or spill slot
jlaskey@80 265 */
jlaskey@80 266 public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot) {
jlaskey@80 267 super(key, flags, slot);
jlaskey@80 268
hannesw@828 269 initGetterSetter(structure);
attila@963 270 initializeType();
hannesw@828 271 }
hannesw@828 272
hannesw@828 273 private void initGetterSetter(final Class<?> structure) {
hannesw@828 274 final int slot = getSlot();
jlaskey@80 275 /*
jlaskey@80 276 * primitiveGetter and primitiveSetter are only used in dual fields mode. Setting them to null also
jlaskey@80 277 * works in dual field mode, it only means that the property never has a primitive
jlaskey@80 278 * representation.
jlaskey@80 279 */
jlaskey@80 280
jlaskey@80 281 if (isParameter() && hasArguments()) {
attila@963 282 //parameters are always stored in an object array, which may or may not be a good idea
attila@963 283 final MethodHandle arguments = MH.getter(LOOKUP, structure, "arguments", ScriptObject.class);
attila@422 284 objectGetter = MH.asType(MH.insertArguments(MH.filterArguments(ScriptObject.GET_ARGUMENT.methodHandle(), 0, arguments), 1, slot), Lookup.GET_OBJECT_TYPE);
attila@422 285 objectSetter = MH.asType(MH.insertArguments(MH.filterArguments(ScriptObject.SET_ARGUMENT.methodHandle(), 0, arguments), 1, slot), Lookup.SET_OBJECT_TYPE);
attila@963 286 primitiveGetter = null;
attila@963 287 primitiveSetter = null;
jlaskey@80 288 } else {
attila@963 289 final Accessors gs = GETTERS_SETTERS.get(structure);
attila@963 290 objectGetter = gs.objectGetters[slot];
attila@963 291 primitiveGetter = gs.primitiveGetters[slot];
attila@963 292 objectSetter = gs.objectSetters[slot];
attila@963 293 primitiveSetter = gs.primitiveSetters[slot];
jlaskey@80 294 }
jlaskey@80 295 }
jlaskey@80 296
jlaskey@80 297 /**
attila@963 298 * Constructor
jlaskey@3 299 *
attila@963 300 * @param key key
attila@963 301 * @param flags flags
attila@963 302 * @param slot field slot index
attila@963 303 * @param owner owner of property
attila@963 304 * @param initialValue initial value to which the property can be set
jlaskey@3 305 */
attila@963 306 protected AccessorProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
attila@963 307 this(key, flags, owner.getClass(), slot);
attila@963 308 setInitialValue(owner, initialValue);
attila@963 309 }
jlaskey@3 310
attila@963 311 /**
attila@963 312 * Normal access property constructor that overrides the type
attila@963 313 * Override the initial type. Used for Object Literals
attila@963 314 *
attila@963 315 * @param key key
attila@963 316 * @param flags flags
attila@963 317 * @param structure structure to JO subclass
attila@963 318 * @param slot field slot index
attila@963 319 * @param initialType initial type of the property
attila@963 320 */
attila@963 321 public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) {
attila@963 322 this(key, flags, structure, slot);
attila@963 323 setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : initialType);
attila@963 324 }
attila@963 325
attila@963 326 /**
attila@963 327 * Copy constructor that may change type and in that case clear the cache. Important to do that before
attila@963 328 * type change or getters will be created already stale.
attila@963 329 *
attila@963 330 * @param property property
attila@963 331 * @param newType new type
attila@963 332 */
attila@963 333 protected AccessorProperty(final AccessorProperty property, final Class<?> newType) {
attila@963 334 super(property, property.getFlags());
attila@963 335
attila@963 336 this.GETTER_CACHE = newType != property.getCurrentType() ? new MethodHandle[NOOF_TYPES] : property.GETTER_CACHE;
jlaskey@3 337 this.primitiveGetter = property.primitiveGetter;
jlaskey@3 338 this.primitiveSetter = property.primitiveSetter;
jlaskey@3 339 this.objectGetter = property.objectGetter;
jlaskey@3 340 this.objectSetter = property.objectSetter;
jlaskey@3 341
attila@963 342 setCurrentType(newType);
attila@963 343 }
attila@963 344
attila@963 345 /**
attila@963 346 * COPY constructor
attila@963 347 *
attila@963 348 * @param property source property
attila@963 349 */
attila@963 350 protected AccessorProperty(final AccessorProperty property) {
attila@963 351 this(property, property.getCurrentType());
attila@963 352 }
attila@963 353
attila@963 354 /**
attila@963 355 * Set initial value of a script object's property
attila@963 356 * @param owner owner
attila@963 357 * @param initialValue initial value
attila@963 358 */
attila@963 359 protected final void setInitialValue(final ScriptObject owner, final Object initialValue) {
attila@963 360 setCurrentType(JSType.unboxedFieldType(initialValue));
attila@963 361 if (initialValue instanceof Integer) {
attila@963 362 invokeSetter(owner, ((Integer)initialValue).intValue());
attila@963 363 } else if (initialValue instanceof Long) {
attila@963 364 invokeSetter(owner, ((Long)initialValue).longValue());
attila@963 365 } else if (initialValue instanceof Double) {
attila@963 366 invokeSetter(owner, ((Double)initialValue).doubleValue());
attila@963 367 } else {
attila@963 368 invokeSetter(owner, initialValue);
attila@963 369 }
attila@963 370 }
attila@963 371
attila@963 372 /**
attila@963 373 * Initialize the type of a property
attila@963 374 */
attila@963 375 protected final void initializeType() {
attila@963 376 setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : null);
jlaskey@3 377 }
jlaskey@3 378
hannesw@828 379 private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException {
hannesw@828 380 s.defaultReadObject();
hannesw@828 381 // Restore getters array
attila@963 382 GETTER_CACHE = new MethodHandle[NOOF_TYPES];
hannesw@828 383 }
hannesw@828 384
jlaskey@3 385 private static MethodHandle bindTo(final MethodHandle mh, final Object receiver) {
jlaskey@3 386 if (mh == null) {
jlaskey@3 387 return null;
jlaskey@3 388 }
jlaskey@3 389
jlaskey@3 390 return MH.dropArguments(MH.bindTo(mh, receiver), 0, Object.class);
jlaskey@3 391 }
jlaskey@3 392
jlaskey@3 393 @Override
attila@963 394 public Property copy() {
jlaskey@3 395 return new AccessorProperty(this);
jlaskey@3 396 }
jlaskey@3 397
jlaskey@3 398 @Override
attila@963 399 public Property copy(final Class<?> newType) {
attila@963 400 return new AccessorProperty(this, newType);
hannesw@291 401 }
hannesw@291 402
hannesw@291 403 @Override
attila@963 404 public int getIntValue(final ScriptObject self, final ScriptObject owner) {
attila@963 405 try {
attila@963 406 return (int)getGetter(int.class).invokeExact((Object)self);
attila@963 407 } catch (final Error | RuntimeException e) {
attila@963 408 throw e;
attila@963 409 } catch (final Throwable e) {
attila@963 410 throw new RuntimeException(e);
lagergren@295 411 }
attila@963 412 }
lagergren@295 413
attila@963 414 @Override
attila@963 415 public long getLongValue(final ScriptObject self, final ScriptObject owner) {
lagergren@295 416 try {
attila@963 417 return (long)getGetter(long.class).invokeExact((Object)self);
attila@963 418 } catch (final Error | RuntimeException e) {
lagergren@295 419 throw e;
lagergren@295 420 } catch (final Throwable e) {
lagergren@295 421 throw new RuntimeException(e);
hannesw@291 422 }
hannesw@291 423 }
hannesw@291 424
attila@963 425 @Override
attila@963 426 public double getDoubleValue(final ScriptObject self, final ScriptObject owner) {
attila@963 427 try {
attila@963 428 return (double)getGetter(double.class).invokeExact((Object)self);
attila@963 429 } catch (final Error | RuntimeException e) {
attila@963 430 throw e;
attila@963 431 } catch (final Throwable e) {
attila@963 432 throw new RuntimeException(e);
hannesw@291 433 }
hannesw@415 434 }
hannesw@415 435
attila@963 436 @Override
attila@963 437 public Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
attila@963 438 try {
attila@963 439 return getGetter(Object.class).invokeExact((Object)self);
attila@963 440 } catch (final Error | RuntimeException e) {
attila@963 441 throw e;
attila@963 442 } catch (final Throwable e) {
attila@963 443 throw new RuntimeException(e);
hannesw@415 444 }
attila@963 445 }
attila@963 446
attila@963 447 /**
attila@963 448 * Invoke setter for this property with a value
attila@963 449 * @param self owner
attila@963 450 * @param value value
attila@963 451 */
attila@963 452 protected final void invokeSetter(final ScriptObject self, final int value) {
attila@963 453 try {
attila@963 454 getSetter(int.class, self.getMap()).invokeExact((Object)self, value);
attila@963 455 } catch (final Error | RuntimeException e) {
attila@963 456 throw e;
attila@963 457 } catch (final Throwable e) {
attila@963 458 throw new RuntimeException(e);
attila@963 459 }
attila@963 460 }
attila@963 461
attila@963 462 /**
attila@963 463 * Invoke setter for this property with a value
attila@963 464 * @param self owner
attila@963 465 * @param value value
attila@963 466 */
attila@963 467 protected final void invokeSetter(final ScriptObject self, final long value) {
attila@963 468 try {
attila@963 469 getSetter(long.class, self.getMap()).invokeExact((Object)self, value);
attila@963 470 } catch (final Error | RuntimeException e) {
attila@963 471 throw e;
attila@963 472 } catch (final Throwable e) {
attila@963 473 throw new RuntimeException(e);
attila@963 474 }
attila@963 475 }
attila@963 476
attila@963 477 /**
attila@963 478 * Invoke setter for this property with a value
attila@963 479 * @param self owner
attila@963 480 * @param value value
attila@963 481 */
attila@963 482 protected final void invokeSetter(final ScriptObject self, final double value) {
attila@963 483 try {
attila@963 484 getSetter(double.class, self.getMap()).invokeExact((Object)self, value);
attila@963 485 } catch (final Error | RuntimeException e) {
attila@963 486 throw e;
attila@963 487 } catch (final Throwable e) {
attila@963 488 throw new RuntimeException(e);
attila@963 489 }
attila@963 490 }
attila@963 491
attila@963 492 /**
attila@963 493 * Invoke setter for this property with a value
attila@963 494 * @param self owner
attila@963 495 * @param value value
attila@963 496 */
attila@963 497 protected final void invokeSetter(final ScriptObject self, final Object value) {
attila@963 498 try {
attila@963 499 getSetter(Object.class, self.getMap()).invokeExact((Object)self, value);
attila@963 500 } catch (final Error | RuntimeException e) {
attila@963 501 throw e;
attila@963 502 } catch (final Throwable e) {
attila@963 503 throw new RuntimeException(e);
attila@963 504 }
attila@963 505 }
attila@963 506
attila@963 507 @Override
attila@963 508 public void setValue(final ScriptObject self, final ScriptObject owner, final int value, final boolean strict) {
attila@963 509 assert isConfigurable() || isWritable() : getKey() + " is not writable or configurable";
attila@963 510 invokeSetter(self, value);
attila@963 511 }
attila@963 512
attila@963 513 @Override
attila@963 514 public void setValue(final ScriptObject self, final ScriptObject owner, final long value, final boolean strict) {
attila@963 515 assert isConfigurable() || isWritable() : getKey() + " is not writable or configurable";
attila@963 516 invokeSetter(self, value);
attila@963 517 }
attila@963 518
attila@963 519 @Override
attila@963 520 public void setValue(final ScriptObject self, final ScriptObject owner, final double value, final boolean strict) {
attila@963 521 assert isConfigurable() || isWritable() : getKey() + " is not writable or configurable";
attila@963 522 invokeSetter(self, value);
attila@963 523 }
attila@963 524
attila@963 525 @Override
attila@963 526 public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
attila@963 527 //this is sometimes used for bootstrapping, hence no assert. ugly.
attila@963 528 invokeSetter(self, value);
hannesw@415 529 }
hannesw@415 530
hannesw@415 531 @Override
hannesw@828 532 void initMethodHandles(final Class<?> structure) {
attila@963 533 // sanity check for structure class
hannesw@828 534 if (!ScriptObject.class.isAssignableFrom(structure) || !StructureLoader.isStructureClass(structure.getName())) {
hannesw@828 535 throw new IllegalArgumentException();
hannesw@828 536 }
attila@963 537 // this method is overridden in SpillProperty
attila@963 538 assert !isSpill();
attila@963 539 initGetterSetter(structure);
hannesw@828 540 }
hannesw@828 541
hannesw@828 542 @Override
hannesw@415 543 public MethodHandle getGetter(final Class<?> type) {
jlaskey@3 544 final int i = getAccessorTypeIndex(type);
hannesw@415 545
attila@963 546 assert type == int.class ||
attila@963 547 type == long.class ||
attila@963 548 type == double.class ||
attila@963 549 type == Object.class :
attila@963 550 "invalid getter type " + type + " for " + getKey();
attila@963 551
hannesw@991 552 checkUndeclared();
hannesw@991 553
attila@963 554 //all this does is add a return value filter for object fields only
attila@963 555 final MethodHandle[] getterCache = GETTER_CACHE;
attila@963 556 final MethodHandle cachedGetter = getterCache[i];
attila@963 557 final MethodHandle getter;
attila@963 558 if (cachedGetter != null) {
attila@963 559 getter = cachedGetter;
attila@963 560 } else {
attila@963 561 getter = debug(
attila@963 562 createGetter(
attila@963 563 getCurrentType(),
attila@963 564 type,
attila@963 565 primitiveGetter,
attila@963 566 objectGetter,
attila@963 567 INVALID_PROGRAM_POINT),
attila@963 568 getCurrentType(),
attila@963 569 type,
attila@963 570 "get");
attila@963 571 getterCache[i] = getter;
attila@963 572 }
attila@963 573 assert getter.type().returnType() == type && getter.type().parameterType(0) == Object.class;
attila@963 574 return getter;
attila@963 575 }
attila@963 576
attila@963 577 @Override
attila@963 578 public MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint) {
attila@963 579 // nasgen generated primitive fields like Math.PI have only one known unchangeable primitive type
attila@963 580 if (objectGetter == null) {
attila@963 581 return getOptimisticPrimitiveGetter(type, programPoint);
jlaskey@3 582 }
jlaskey@3 583
hannesw@991 584 checkUndeclared();
hannesw@991 585
attila@963 586 return debug(
attila@963 587 createGetter(
attila@963 588 getCurrentType(),
attila@963 589 type,
attila@963 590 primitiveGetter,
attila@963 591 objectGetter,
attila@963 592 programPoint),
attila@963 593 getCurrentType(),
attila@963 594 type,
attila@963 595 "get");
attila@963 596 }
attila@963 597
attila@963 598 private MethodHandle getOptimisticPrimitiveGetter(final Class<?> type, final int programPoint) {
attila@963 599 final MethodHandle g = getGetter(getCurrentType());
attila@963 600 return MH.asType(OptimisticReturnFilters.filterOptimisticReturnValue(g, type, programPoint), g.type().changeReturnType(type));
jlaskey@3 601 }
jlaskey@3 602
jlaskey@3 603 private Property getWiderProperty(final Class<?> type) {
attila@963 604 return copy(type); //invalidate cache of new property
attila@963 605
jlaskey@3 606 }
jlaskey@3 607
jlaskey@3 608 private PropertyMap getWiderMap(final PropertyMap oldMap, final Property newProperty) {
jlaskey@3 609 final PropertyMap newMap = oldMap.replaceProperty(this, newProperty);
jlaskey@3 610 assert oldMap.size() > 0;
jlaskey@3 611 assert newMap.size() == oldMap.size();
jlaskey@3 612 return newMap;
jlaskey@3 613 }
jlaskey@3 614
hannesw@991 615 private void checkUndeclared() {
hannesw@991 616 if ((getFlags() & NEEDS_DECLARATION) != 0) {
hannesw@991 617 // a lexically defined variable that hasn't seen its declaration - throw ReferenceError
hannesw@991 618 throw ECMAErrors.referenceError("not.defined", getKey());
hannesw@991 619 }
hannesw@991 620 }
hannesw@991 621
jlaskey@3 622 // the final three arguments are for debug printout purposes only
jlaskey@3 623 @SuppressWarnings("unused")
attila@963 624 private static Object replaceMap(final Object sobj, final PropertyMap newMap) {
jlaskey@3 625 ((ScriptObject)sobj).setMap(newMap);
jlaskey@3 626 return sobj;
jlaskey@3 627 }
jlaskey@3 628
attila@963 629 @SuppressWarnings("unused")
attila@963 630 private static Object invalidateSwitchPoint(final Object obj, final SwitchPoint sp) {
attila@963 631 SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
attila@963 632 return obj;
attila@963 633 }
attila@963 634
jlaskey@3 635 private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) {
attila@963 636 return debug(createSetter(forType, type, primitiveSetter, objectSetter), getCurrentType(), type, "set");
attila@963 637 }
attila@963 638
attila@963 639 /**
attila@963 640 * Is this property of the undefined type?
attila@963 641 * @return true if undefined
attila@963 642 */
attila@963 643 protected final boolean isUndefined() {
attila@963 644 return getCurrentType() == null;
jlaskey@3 645 }
jlaskey@3 646
jlaskey@3 647 @Override
jlaskey@3 648 public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) {
hannesw@991 649 checkUndeclared();
hannesw@991 650
hannesw@991 651 final int typeIndex = getAccessorTypeIndex(type);
hannesw@991 652 final int currentTypeIndex = getAccessorTypeIndex(getCurrentType());
jlaskey@3 653
jlaskey@3 654 //if we are asking for an object setter, but are still a primitive type, we might try to box it
jlaskey@242 655 MethodHandle mh;
hannesw@991 656 if (needsInvalidator(typeIndex, currentTypeIndex)) {
jlaskey@3 657 final Property newProperty = getWiderProperty(type);
jlaskey@3 658 final PropertyMap newMap = getWiderMap(currentMap, newProperty);
attila@963 659
jlaskey@3 660 final MethodHandle widerSetter = newProperty.getSetter(type, newMap);
attila@963 661 final Class<?> ct = getCurrentType();
attila@963 662 mh = MH.filterArguments(widerSetter, 0, MH.insertArguments(debugReplace(ct, type, currentMap, newMap) , 1, newMap));
attila@963 663 if (ct != null && ct.isPrimitive() && !type.isPrimitive()) {
attila@963 664 mh = ObjectClassGenerator.createGuardBoxedPrimitiveSetter(ct, generateSetter(ct, ct), mh);
jlaskey@3 665 }
jlaskey@242 666 } else {
hannesw@991 667 final Class<?> forType = isUndefined() ? type : getCurrentType();
attila@963 668 mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type);
jlaskey@3 669 }
jlaskey@3 670
attila@963 671 /**
attila@963 672 * Check if this is a special global name that requires switchpoint invalidation
attila@963 673 */
attila@963 674 final SwitchPoint ccb = getChangeCallback();
attila@963 675 if (ccb != null && ccb != NO_CHANGE_CALLBACK) {
attila@963 676 mh = MH.filterArguments(mh, 0, MH.insertArguments(debugInvalidate(getKey(), ccb), 1, changeCallback));
attila@963 677 }
attila@963 678
attila@963 679 assert mh.type().returnType() == void.class : mh.type();
attila@963 680
hannesw@291 681 return mh;
jlaskey@3 682 }
jlaskey@3 683
attila@963 684 /**
attila@963 685 * Get the change callback for this property
attila@963 686 * @return switchpoint that is invalidated when property changes
attila@963 687 */
attila@963 688 protected SwitchPoint getChangeCallback() {
attila@963 689 if (changeCallback == null) {
attila@963 690 try {
attila@963 691 changeCallback = Global.instance().getChangeCallback(getKey());
attila@963 692 } catch (final NullPointerException e) {
attila@963 693 assert !"apply".equals(getKey()) && !"call".equals(getKey());
attila@963 694 //empty
attila@963 695 }
attila@963 696 if (changeCallback == null) {
attila@963 697 changeCallback = NO_CHANGE_CALLBACK;
attila@963 698 }
attila@963 699 }
attila@963 700 return changeCallback;
attila@963 701 }
attila@963 702
jlaskey@3 703 @Override
attila@963 704 public final boolean canChangeType() {
jlaskey@3 705 if (OBJECT_FIELDS_ONLY) {
jlaskey@3 706 return false;
jlaskey@3 707 }
hannesw@991 708 // Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST.
hannesw@991 709 return getCurrentType() == null || (getCurrentType() != Object.class && (isConfigurable() || isWritable()));
jlaskey@3 710 }
jlaskey@3 711
hannesw@991 712 private boolean needsInvalidator(final int typeIndex, final int currentTypeIndex) {
hannesw@991 713 return canChangeType() && typeIndex > currentTypeIndex;
jlaskey@3 714 }
jlaskey@3 715
attila@963 716 @Override
attila@963 717 public final void setCurrentType(final Class<?> currentType) {
attila@963 718 assert currentType != boolean.class : "no boolean storage support yet - fix this";
attila@963 719 this.currentType = currentType == null ? null : currentType.isPrimitive() ? currentType : Object.class;
jlaskey@3 720 }
jlaskey@3 721
jlaskey@3 722 @Override
jlaskey@3 723 public Class<?> getCurrentType() {
jlaskey@3 724 return currentType;
jlaskey@3 725 }
jlaskey@3 726
attila@963 727
attila@963 728 private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) {
attila@963 729 if (!Context.DEBUG || !Global.hasInstance()) {
attila@963 730 return mh;
attila@963 731 }
attila@963 732
attila@963 733 final Context context = Context.getContextTrusted();
attila@963 734 assert context != null;
attila@963 735
attila@963 736 return context.addLoggingToHandle(
attila@963 737 ObjectClassGenerator.class,
attila@963 738 Level.INFO,
attila@963 739 mh,
attila@963 740 0,
attila@963 741 true,
attila@963 742 new Supplier<String>() {
attila@963 743 @Override
attila@963 744 public String get() {
attila@963 745 return tag + " '" + getKey() + "' (property="+ Debug.id(this) + ", slot=" + getSlot() + " " + getClass().getSimpleName() + " forType=" + stripName(forType) + ", type=" + stripName(type) + ')';
attila@963 746 }
attila@963 747 });
jlaskey@3 748 }
jlaskey@3 749
attila@963 750 private MethodHandle debugReplace(final Class<?> oldType, final Class<?> newType, final PropertyMap oldMap, final PropertyMap newMap) {
attila@963 751 if (!Context.DEBUG || !Global.hasInstance()) {
attila@963 752 return REPLACE_MAP;
attila@963 753 }
attila@963 754
attila@963 755 final Context context = Context.getContextTrusted();
attila@963 756 assert context != null;
attila@963 757
attila@963 758 MethodHandle mh = context.addLoggingToHandle(
attila@963 759 ObjectClassGenerator.class,
attila@963 760 REPLACE_MAP,
attila@963 761 new Supplier<String>() {
attila@963 762 @Override
attila@963 763 public String get() {
attila@963 764 return "Type change for '" + getKey() + "' " + oldType + "=>" + newType;
attila@963 765 }
attila@963 766 });
attila@963 767
attila@963 768 mh = context.addLoggingToHandle(
attila@963 769 ObjectClassGenerator.class,
attila@963 770 Level.FINEST,
attila@963 771 mh,
attila@963 772 Integer.MAX_VALUE,
attila@963 773 false,
attila@963 774 new Supplier<String>() {
attila@963 775 @Override
attila@963 776 public String get() {
attila@963 777 return "Setting map " + Debug.id(oldMap) + " => " + Debug.id(newMap) + " " + oldMap + " => " + newMap;
attila@963 778 }
attila@963 779 });
attila@963 780 return mh;
attila@963 781 }
attila@963 782
attila@963 783 private static MethodHandle debugInvalidate(final String key, final SwitchPoint sp) {
attila@963 784 if (!Context.DEBUG || !Global.hasInstance()) {
attila@963 785 return INVALIDATE_SP;
attila@963 786 }
attila@963 787
attila@963 788 final Context context = Context.getContextTrusted();
attila@963 789 assert context != null;
attila@963 790
attila@963 791 return context.addLoggingToHandle(
attila@963 792 ObjectClassGenerator.class,
attila@963 793 INVALIDATE_SP,
attila@963 794 new Supplier<String>() {
attila@963 795 @Override
attila@963 796 public String get() {
attila@963 797 return "Field change callback for " + key + " triggered: " + sp;
attila@963 798 }
attila@963 799 });
attila@963 800 }
attila@963 801
attila@963 802 private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
attila@963 803 return MH.findStatic(LOOKUP, AccessorProperty.class, name, MH.type(rtype, types));
attila@963 804 }
jlaskey@3 805 }

mercurial