1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.runtime; 27 28 import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE; 29 import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE; 30 import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE; 31 32 import java.lang.invoke.MethodHandle; 33 import java.util.Objects; 34 import jdk.nashorn.internal.codegen.ObjectClassGenerator; 35 import jdk.nashorn.internal.codegen.types.Type; 36 37 /** 38 * This is the abstract superclass representing a JavaScript Property. 39 * The {@link PropertyMap} map links keys to properties, and consequently 40 * instances of this class make up the values in the PropertyMap 41 * 42 * @see PropertyMap 43 * @see AccessorProperty 44 * @see UserAccessorProperty 45 */ 46 public abstract class Property { 47 /* 48 * ECMA 8.6.1 Property Attributes 49 * 50 * We use negative flags because most properties are expected to 51 * be 'writable', 'configurable' and 'enumerable'. With negative flags, 52 * we can use leave flag byte initialized with (the default) zero value. 53 */ 54 55 /** ECMA 8.6.1 - Is this property not writable? */ 56 public static final int NOT_WRITABLE = 0b0000_0000_0001; 57 58 /** ECMA 8.6.1 - Is this property not enumerable? */ 59 public static final int NOT_ENUMERABLE = 0b0000_0000_0010; 60 61 /** ECMA 8.6.1 - Is this property not configurable? */ 62 public static final int NOT_CONFIGURABLE = 0b0000_0000_0100; 63 64 private static final int MODIFY_MASK = 0b0000_0000_1111; 65 66 /** Is this a spill property? See {@link AccessorProperty} */ 67 public static final int IS_SPILL = 0b0000_0001_0000; 68 69 /** Is this a function parameter? */ 70 public static final int IS_PARAMETER = 0b0000_0010_0000; 71 72 /** Is parameter accessed thru arguments? */ 73 public static final int HAS_ARGUMENTS = 0b0000_0100_0000; 74 75 /** Is this property always represented as an Object? See {@link ObjectClassGenerator} and dual fields flag. */ 76 public static final int IS_ALWAYS_OBJECT = 0b0000_1000_0000; 77 78 /** Can this property be primitive? */ 79 public static final int CAN_BE_PRIMITIVE = 0b0001_0000_0000; 80 81 /** Can this property be undefined? */ 82 public static final int CAN_BE_UNDEFINED = 0b0010_0000_0000; 83 84 /** Property key. */ 85 private final String key; 86 87 /** Property flags. */ 88 protected int flags; 89 90 /** Property field number or spill slot. */ 91 private final int slot; 92 93 /** 94 * Constructor 95 * 96 * @param key property key 97 * @param flags property flags 98 * @param slot property field number or spill slot 99 */ 100 public Property(final String key, final int flags, final int slot) { 101 assert key != null; 102 this.key = key; 103 this.flags = flags; 104 this.slot = slot; 105 } 106 107 /** 108 * Copy constructor 109 * 110 * @param property source property 111 */ 112 protected Property(final Property property) { 113 this.key = property.key; 114 this.flags = property.flags; 115 this.slot = property.slot; 116 } 117 118 /** 119 * Copy function 120 * 121 * @return cloned property 122 */ 123 protected abstract Property copy(); 124 125 /** 126 * Property flag utility method for {@link PropertyDescriptor}s. Given two property descriptors, 127 * return the result of merging their flags. 128 * 129 * @param oldDesc first property descriptor 130 * @param newDesc second property descriptor 131 * @return merged flags. 132 */ 133 static int mergeFlags(final PropertyDescriptor oldDesc, final PropertyDescriptor newDesc) { 134 int propFlags = 0; 135 boolean value; 136 137 value = newDesc.has(CONFIGURABLE) ? newDesc.isConfigurable() : oldDesc.isConfigurable(); 138 if (!value) { 139 propFlags |= NOT_CONFIGURABLE; 140 } 141 142 value = newDesc.has(ENUMERABLE) ? newDesc.isEnumerable() : oldDesc.isEnumerable(); 143 if (!value) { 144 propFlags |= NOT_ENUMERABLE; 145 } 146 147 value = newDesc.has(WRITABLE) ? newDesc.isWritable() : oldDesc.isWritable(); 148 if (!value) { 149 propFlags |= NOT_WRITABLE; 150 } 151 152 return propFlags; 153 } 154 155 /** 156 * Property flag utility method for {@link PropertyDescriptor}. Get the property flags 157 * conforming to any Property using this PropertyDescriptor 158 * 159 * @param desc property descriptor 160 * @return flags for properties that conform to property descriptor 161 */ 162 static int toFlags(final PropertyDescriptor desc) { 163 int propFlags = 0; 164 165 if (!desc.isConfigurable()) { 166 propFlags |= NOT_CONFIGURABLE; 167 } 168 if (!desc.isEnumerable()) { 169 propFlags |= NOT_ENUMERABLE; 170 } 171 if (!desc.isWritable()) { 172 propFlags |= NOT_WRITABLE; 173 } 174 175 return propFlags; 176 } 177 178 /** 179 * Check whether this property has a user defined getter function. See {@link UserAccessorProperty} 180 * @return true if getter function exists, false is default 181 */ 182 public boolean hasGetterFunction() { 183 return false; 184 } 185 186 /** 187 * Check whether this property has a user defined setter function. See {@link UserAccessorProperty} 188 * @return true if getter function exists, false is default 189 */ 190 public boolean hasSetterFunction() { 191 return false; 192 } 193 194 /** 195 * Check whether this property is writable (see ECMA 8.6.1) 196 * @return true if writable 197 */ 198 public boolean isWritable() { 199 return (flags & NOT_WRITABLE) == 0; 200 } 201 202 /** 203 * Check whether this property is writable (see ECMA 8.6.1) 204 * @return true if configurable 205 */ 206 public boolean isConfigurable() { 207 return (flags & NOT_CONFIGURABLE) == 0; 208 } 209 210 /** 211 * Check whether this property is enumerable (see ECMA 8.6.1) 212 * @return true if enumerable 213 */ 214 public boolean isEnumerable() { 215 return (flags & NOT_ENUMERABLE) == 0; 216 } 217 218 /** 219 * Check whether this property is used as a function parameter 220 * @return true if parameter 221 */ 222 public boolean isParameter() { 223 return (flags & IS_PARAMETER) == IS_PARAMETER; 224 } 225 226 /** 227 * Check whether this property is in an object with arguments field 228 * @return true if has arguments 229 */ 230 public boolean hasArguments() { 231 return (flags & HAS_ARGUMENTS) == HAS_ARGUMENTS; 232 } 233 234 /** 235 * Check whether this is a spill property, i.e. one that will not 236 * be stored in a specially generated field in the property class. 237 * The spill pool is maintained separately, as a growing Object array 238 * in the {@link ScriptObject}. 239 * 240 * @return true if spill property 241 */ 242 public boolean isSpill() { 243 return (flags & IS_SPILL) == IS_SPILL; 244 } 245 246 /** 247 * Does this property use any slots in the spill array described in 248 * {@link Property#isSpill}? In that case how many. Currently a property 249 * only uses max one spill slot, but this may change in future representations 250 * Only {@link AccessorProperty} instances use spill slots 251 * 252 * @return number of spill slots a property is using 253 */ 254 public int getSpillCount() { 255 return isSpill() ? 1 : 0; 256 } 257 258 /** 259 * Add more property flags to the property. Properties are immutable here, 260 * so any property change that results in a larger flag set results in the 261 * property being cloned. Use only the return value 262 * 263 * @param propertyFlags flags to be OR:ed to the existing property flags 264 * @return new property if property set was changed, {@code this} otherwise 265 */ 266 public Property addFlags(final int propertyFlags) { 267 if ((this.flags & propertyFlags) != propertyFlags) { 268 final Property cloned = this.copy(); 269 cloned.flags |= propertyFlags; 270 return cloned; 271 } 272 return this; 273 } 274 275 /** 276 * Get the flags for this property 277 * @return property flags 278 */ 279 public int getFlags() { 280 return flags; 281 } 282 283 /** 284 * Get the modify flags for this property. The modify flags are the ECMA 8.6.1 285 * flags that decide if the Property is writable, configurable and/or enumerable. 286 * 287 * @return modify flags for property 288 */ 289 public int getModifyFlags() { 290 return flags & MODIFY_MASK; 291 } 292 293 /** 294 * Remove property flags from the property. Properties are immutable here, 295 * so any property change that results in a smaller flag set results in the 296 * property being cloned. Use only the return value 297 * 298 * @param propertyFlags flags to be subtracted from the existing property flags 299 * @return new property if property set was changed, {@code this} otherwise 300 */ 301 public Property removeFlags(final int propertyFlags) { 302 if ((this.flags & propertyFlags) != 0) { 303 final Property cloned = this.copy(); 304 cloned.flags &= ~propertyFlags; 305 return cloned; 306 } 307 return this; 308 } 309 310 /** 311 * Reset the property for this property. Properties are immutable here, 312 * so any property change that results in a different flag sets results in the 313 * property being cloned. Use only the return value 314 * 315 * @param propertyFlags flags that are replacing from the existing property flags 316 * @return new property if property set was changed, {@code this} otherwise 317 */ 318 public Property setFlags(final int propertyFlags) { 319 if (this.flags != propertyFlags) { 320 final Property cloned = this.copy(); 321 cloned.flags &= ~MODIFY_MASK; 322 cloned.flags |= propertyFlags & MODIFY_MASK; 323 return cloned; 324 } 325 return this; 326 } 327 328 /** 329 * Abstract method for retrieving the getter for the property. We do not know 330 * anything about the internal representation when we request the getter, we only 331 * know that the getter will return the property as the given type. 332 * 333 * @param type getter return value type 334 * @return a getter for this property as {@code type} 335 */ 336 public abstract MethodHandle getGetter(final Class<?> type); 337 338 /** 339 * Get the key for this property. This key is an ordinary string. The "name". 340 * @return key for property 341 */ 342 public String getKey() { 343 return key; 344 } 345 346 /** 347 * Get the field number or spill slot 348 * @return number/slot, -1 if none exists 349 */ 350 public int getSlot() { 351 return slot; 352 } 353 354 /** 355 * Abstract method for retrieving the setter for the property. We do not know 356 * anything about the internal representation when we request the setter, we only 357 * know that the setter will take the property as a parameter of the given type. 358 * <p> 359 * Note that we have to pass the current property map from which we retrieved 360 * the property here. This is necessary for map guards if, e.g. the internal 361 * representation of the field, and consequently also the setter, changes. Then 362 * we automatically get a map guard that relinks the call site so that the 363 * older setter will never be used again. 364 * <p> 365 * see {@link ObjectClassGenerator#createSetter(Class, Class, MethodHandle, MethodHandle)} 366 * if you are interested in the internal details of this. Note that if you 367 * are running in default mode, with {@code -Dnashorn.fields.dual=true}, disabled, the setters 368 * will currently never change, as all properties are represented as Object field, 369 * the Object fields are Initialized to {@code ScriptRuntime.UNDEFINED} and primitives are 370 * boxed/unboxed upon every access, which is not necessarily optimal 371 * 372 * @param type setter parameter type 373 * @param currentMap current property map for property 374 * @return a getter for this property as {@code type} 375 */ 376 public abstract MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap); 377 378 /** 379 * Get the user defined getter function if one exists. Only {@link UserAccessorProperty} instances 380 * can have user defined getters 381 * @param obj the script object 382 * @return user defined getter function, or {@code null} if none exists 383 */ 384 public ScriptFunction getGetterFunction(final ScriptObject obj) { 385 return null; 386 } 387 388 /** 389 * Get the user defined setter function if one exists. Only {@link UserAccessorProperty} instances 390 * can have user defined getters 391 * @param obj the script object 392 * @return user defined getter function, or {@code null} if none exists 393 */ 394 public ScriptFunction getSetterFunction(final ScriptObject obj) { 395 return null; 396 } 397 398 @Override 399 public int hashCode() { 400 final Class<?> type = getCurrentType(); 401 return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (type == null ? 0 : type.hashCode()); 402 } 403 404 @Override 405 public boolean equals(final Object other) { 406 if (this == other) { 407 return true; 408 } 409 410 if (other == null || this.getClass() != other.getClass()) { 411 return false; 412 } 413 414 final Property otherProperty = (Property)other; 415 416 return getFlags() == otherProperty.getFlags() && 417 getSlot() == otherProperty.getSlot() && 418 getCurrentType() == otherProperty.getCurrentType() && 419 getKey().equals(otherProperty.getKey()); 420 } 421 422 @Override 423 public String toString() { 424 final StringBuilder sb = new StringBuilder(); 425 final Class<?> type = getCurrentType(); 426 427 sb.append(getKey()). 428 append("(0x"). 429 append(Integer.toHexString(flags)). 430 append(") "). 431 append(getClass().getSimpleName()). 432 append(" {"). 433 append(type == null ? "UNDEFINED" : Type.typeFor(type).getDescriptor()). 434 append('}'); 435 436 if (slot != -1) { 437 sb.append('['); 438 sb.append(slot); 439 sb.append(']'); 440 } 441 442 return sb.toString(); 443 } 444 445 /** 446 * Get the current type of this field. If you are not running with dual fields enabled, 447 * this will always be Object.class. See the value representation explanation in 448 * {@link Property#getSetter(Class, PropertyMap)} and {@link ObjectClassGenerator} 449 * for more information. 450 * 451 * @return current type of property, null means undefined 452 */ 453 public Class<?> getCurrentType() { 454 return Object.class; 455 } 456 457 /** 458 * Check whether this Property can ever change its type. The default is false, and if 459 * you are not running with dual fields, the type is always object and can never change 460 * @return true if this property can change types 461 */ 462 public boolean canChangeType() { 463 return false; 464 } 465 466 /** 467 * Check whether this Property is ever used as anything but an Object. If this is used only 468 * as an object, dual fields mode need not even try to represent it as a primitive at any 469 * callsite, saving map rewrites for performance. 470 * 471 * @return true if representation should always be an object field 472 */ 473 public boolean isAlwaysObject() { 474 return (flags & IS_ALWAYS_OBJECT) == IS_ALWAYS_OBJECT; 475 } 476 477 /** 478 * Check whether this property can be primitive. This is a conservative 479 * analysis result, so {@code false} might mean that it can still be 480 * primitive 481 * 482 * @return can be primitive status 483 */ 484 public boolean canBePrimitive() { 485 return (flags & CAN_BE_PRIMITIVE) == CAN_BE_PRIMITIVE; 486 } 487 488 /** 489 * Check whether this property can be primitive. This is a conservative 490 * analysis result, so {@code true} might mean that it can still be 491 * defined, but it will never say that a property can not be undefined 492 * if it can 493 * 494 * @return can be undefined status 495 */ 496 public boolean canBeUndefined() { 497 return (flags & CAN_BE_UNDEFINED) == CAN_BE_UNDEFINED; 498 } 499 } --- EOF ---