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 /** Mask for property being both writable, enumerable and configurable */ 56 public static final int WRITABLE_ENUMERABLE_CONFIGURABLE = 0b0000_0000_0000; 57 58 /** ECMA 8.6.1 - Is this property not writable? */ 59 public static final int NOT_WRITABLE = 1 << 0; 60 61 /** ECMA 8.6.1 - Is this property not enumerable? */ 62 public static final int NOT_ENUMERABLE = 1 << 1; 63 64 /** ECMA 8.6.1 - Is this property not configurable? */ 65 public static final int NOT_CONFIGURABLE = 1 << 2; 66 67 private static final int MODIFY_MASK = (NOT_WRITABLE | NOT_ENUMERABLE | NOT_CONFIGURABLE); 68 69 /** Is this a spill property? See {@link AccessorProperty} */ 70 public static final int IS_SPILL = 1 << 3; 71 72 /** Is this a function parameter? */ 73 public static final int IS_PARAMETER = 1 << 4; 74 75 /** Is parameter accessed thru arguments? */ 76 public static final int HAS_ARGUMENTS = 1 << 5; 77 78 /** Is this property always represented as an Object? See {@link ObjectClassGenerator} and dual fields flag. */ 79 public static final int IS_ALWAYS_OBJECT = 1 << 6; 80 81 /** Can this property be primitive? */ 82 public static final int CAN_BE_PRIMITIVE = 1 << 7; 83 84 /** Can this property be undefined? */ 85 public static final int CAN_BE_UNDEFINED = 1 << 8; 86 87 /* Is this a function declaration property ? */ 88 public static final int IS_FUNCTION_DECLARATION = 1 << 9; 89 90 /* Is this property bound bound to a receiver? */ 91 public static final int IS_BOUND = 1 << 10; 92 93 /** Property key. */ 94 private final String key; 95 96 /** Property flags. */ 97 protected int flags; 98 99 /** Property field number or spill slot. */ 100 private final int slot; 101 102 /** 103 * Constructor 104 * 105 * @param key property key 106 * @param flags property flags 107 * @param slot property field number or spill slot 108 */ 109 Property(final String key, final int flags, final int slot) { 110 assert key != null; 111 this.key = key; 112 this.flags = flags; 113 this.slot = slot; 114 } 115 116 /** 117 * Copy constructor 118 * 119 * @param property source property 120 */ 121 Property(final Property property) { 122 this.key = property.key; 123 this.flags = property.flags; 124 this.slot = property.slot; 125 } 126 127 /** 128 * Copy function 129 * 130 * @return cloned property 131 */ 132 abstract Property copy(); 133 134 /** 135 * Property flag utility method for {@link PropertyDescriptor}s. Given two property descriptors, 136 * return the result of merging their flags. 137 * 138 * @param oldDesc first property descriptor 139 * @param newDesc second property descriptor 140 * @return merged flags. 141 */ 142 static int mergeFlags(final PropertyDescriptor oldDesc, final PropertyDescriptor newDesc) { 143 int propFlags = 0; 144 boolean value; 145 146 value = newDesc.has(CONFIGURABLE) ? newDesc.isConfigurable() : oldDesc.isConfigurable(); 147 if (!value) { 148 propFlags |= NOT_CONFIGURABLE; 149 } 150 151 value = newDesc.has(ENUMERABLE) ? newDesc.isEnumerable() : oldDesc.isEnumerable(); 152 if (!value) { 153 propFlags |= NOT_ENUMERABLE; 154 } 155 156 value = newDesc.has(WRITABLE) ? newDesc.isWritable() : oldDesc.isWritable(); 157 if (!value) { 158 propFlags |= NOT_WRITABLE; 159 } 160 161 return propFlags; 162 } 163 164 /** 165 * Property flag utility method for {@link PropertyDescriptor}. Get the property flags 166 * conforming to any Property using this PropertyDescriptor 167 * 168 * @param desc property descriptor 169 * @return flags for properties that conform to property descriptor 170 */ 171 static int toFlags(final PropertyDescriptor desc) { 172 int propFlags = 0; 173 174 if (!desc.isConfigurable()) { 175 propFlags |= NOT_CONFIGURABLE; 176 } 177 if (!desc.isEnumerable()) { 178 propFlags |= NOT_ENUMERABLE; 179 } 180 if (!desc.isWritable()) { 181 propFlags |= NOT_WRITABLE; 182 } 183 184 return propFlags; 185 } 186 187 /** 188 * Check whether this property has a user defined getter function. See {@link UserAccessorProperty} 189 * @param obj object containing getter 190 * @return true if getter function exists, false is default 191 */ 192 public boolean hasGetterFunction(final ScriptObject obj) { 193 return false; 194 } 195 196 /** 197 * Check whether this property has a user defined setter function. See {@link UserAccessorProperty} 198 * @param obj object containing setter 199 * @return true if getter function exists, false is default 200 */ 201 public boolean hasSetterFunction(final ScriptObject obj) { 202 return false; 203 } 204 205 /** 206 * Check whether this property is writable (see ECMA 8.6.1) 207 * @return true if writable 208 */ 209 public boolean isWritable() { 210 return (flags & NOT_WRITABLE) == 0; 211 } 212 213 /** 214 * Check whether this property is writable (see ECMA 8.6.1) 215 * @return true if configurable 216 */ 217 public boolean isConfigurable() { 218 return (flags & NOT_CONFIGURABLE) == 0; 219 } 220 221 /** 222 * Check whether this property is enumerable (see ECMA 8.6.1) 223 * @return true if enumerable 224 */ 225 public boolean isEnumerable() { 226 return (flags & NOT_ENUMERABLE) == 0; 227 } 228 229 /** 230 * Check whether this property is used as a function parameter 231 * @return true if parameter 232 */ 233 public boolean isParameter() { 234 return (flags & IS_PARAMETER) == IS_PARAMETER; 235 } 236 237 /** 238 * Check whether this property is in an object with arguments field 239 * @return true if has arguments 240 */ 241 public boolean hasArguments() { 242 return (flags & HAS_ARGUMENTS) == HAS_ARGUMENTS; 243 } 244 245 /** 246 * Check whether this is a spill property, i.e. one that will not 247 * be stored in a specially generated field in the property class. 248 * The spill pool is maintained separately, as a growing Object array 249 * in the {@link ScriptObject}. 250 * 251 * @return true if spill property 252 */ 253 public boolean isSpill() { 254 return (flags & IS_SPILL) == IS_SPILL; 255 } 256 257 /** 258 * Is this property bound to a receiver? 259 * 260 * @return true if this is a bound property 261 */ 262 public boolean isBound() { 263 return (flags & IS_BOUND) == IS_BOUND; 264 } 265 266 /** 267 * Does this property use any slots in the spill array described in 268 * {@link Property#isSpill}? In that case how many. Currently a property 269 * only uses max one spill slot, but this may change in future representations 270 * Only {@link AccessorProperty} instances use spill slots 271 * 272 * @return number of spill slots a property is using 273 */ 274 public int getSpillCount() { 275 return isSpill() ? 1 : 0; 276 } 277 278 /** 279 * Add more property flags to the property. Properties are immutable here, 280 * so any property change that results in a larger flag set results in the 281 * property being cloned. Use only the return value 282 * 283 * @param propertyFlags flags to be OR:ed to the existing property flags 284 * @return new property if property set was changed, {@code this} otherwise 285 */ 286 public Property addFlags(final int propertyFlags) { 287 if ((this.flags & propertyFlags) != propertyFlags) { 288 final Property cloned = this.copy(); 289 cloned.flags |= propertyFlags; 290 return cloned; 291 } 292 return this; 293 } 294 295 /** 296 * Get the flags for this property 297 * @return property flags 298 */ 299 public int getFlags() { 300 return flags; 301 } 302 303 /** 304 * Get the modify flags for this property. The modify flags are the ECMA 8.6.1 305 * flags that decide if the Property is writable, configurable and/or enumerable. 306 * 307 * @return modify flags for property 308 */ 309 public int getModifyFlags() { 310 return flags & MODIFY_MASK; 311 } 312 313 /** 314 * Remove property flags from the property. Properties are immutable here, 315 * so any property change that results in a smaller flag set results in the 316 * property being cloned. Use only the return value 317 * 318 * @param propertyFlags flags to be subtracted from the existing property flags 319 * @return new property if property set was changed, {@code this} otherwise 320 */ 321 public Property removeFlags(final int propertyFlags) { 322 if ((this.flags & propertyFlags) != 0) { 323 final Property cloned = this.copy(); 324 cloned.flags &= ~propertyFlags; 325 return cloned; 326 } 327 return this; 328 } 329 330 /** 331 * Reset the property for this property. Properties are immutable here, 332 * so any property change that results in a different flag sets results in the 333 * property being cloned. Use only the return value 334 * 335 * @param propertyFlags flags that are replacing from the existing property flags 336 * @return new property if property set was changed, {@code this} otherwise 337 */ 338 public Property setFlags(final int propertyFlags) { 339 if (this.flags != propertyFlags) { 340 final Property cloned = this.copy(); 341 cloned.flags &= ~MODIFY_MASK; 342 cloned.flags |= propertyFlags & MODIFY_MASK; 343 return cloned; 344 } 345 return this; 346 } 347 348 /** 349 * Abstract method for retrieving the getter for the property. We do not know 350 * anything about the internal representation when we request the getter, we only 351 * know that the getter will return the property as the given type. 352 * 353 * @param type getter return value type 354 * @return a getter for this property as {@code type} 355 */ 356 public abstract MethodHandle getGetter(final Class<?> type); 357 358 /** 359 * Get the key for this property. This key is an ordinary string. The "name". 360 * @return key for property 361 */ 362 public String getKey() { 363 return key; 364 } 365 366 /** 367 * Get the field number or spill slot 368 * @return number/slot, -1 if none exists 369 */ 370 public int getSlot() { 371 return slot; 372 } 373 374 /** 375 * Set the value of this property in {@code owner}. This allows to bypass creation of the 376 * setter MethodHandle for spill and user accessor properties. 377 * 378 * @param self the this object 379 * @param owner the owner object 380 * @param value the new property value 381 * @param strict is this a strict setter? 382 */ 383 public abstract void setObjectValue(ScriptObject self, ScriptObject owner, Object value, boolean strict); 384 385 /** 386 * Set the Object value of this property from {@code owner}. This allows to bypass creation of the 387 * getter MethodHandle for spill and user accessor properties. 388 * 389 * @param self the this object 390 * @param owner the owner object 391 * @return the property value 392 */ 393 public abstract Object getObjectValue(ScriptObject self, ScriptObject owner); 394 395 /** 396 * Abstract method for retrieving the setter for the property. We do not know 397 * anything about the internal representation when we request the setter, we only 398 * know that the setter will take the property as a parameter of the given type. 399 * <p> 400 * Note that we have to pass the current property map from which we retrieved 401 * the property here. This is necessary for map guards if, e.g. the internal 402 * representation of the field, and consequently also the setter, changes. Then 403 * we automatically get a map guard that relinks the call site so that the 404 * older setter will never be used again. 405 * <p> 406 * see {@link ObjectClassGenerator#createSetter(Class, Class, MethodHandle, MethodHandle)} 407 * if you are interested in the internal details of this. Note that if you 408 * are running in default mode, with {@code -Dnashorn.fields.dual=true}, disabled, the setters 409 * will currently never change, as all properties are represented as Object field, 410 * the Object fields are Initialized to {@code ScriptRuntime.UNDEFINED} and primitives are 411 * boxed/unboxed upon every access, which is not necessarily optimal 412 * 413 * @param type setter parameter type 414 * @param currentMap current property map for property 415 * @return a getter for this property as {@code type} 416 */ 417 public abstract MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap); 418 419 /** 420 * Get the user defined getter function if one exists. Only {@link UserAccessorProperty} instances 421 * can have user defined getters 422 * @param obj the script object 423 * @return user defined getter function, or {@code null} if none exists 424 */ 425 public ScriptFunction getGetterFunction(final ScriptObject obj) { 426 return null; 427 } 428 429 /** 430 * Get the user defined setter function if one exists. Only {@link UserAccessorProperty} instances 431 * can have user defined getters 432 * @param obj the script object 433 * @return user defined getter function, or {@code null} if none exists 434 */ 435 public ScriptFunction getSetterFunction(final ScriptObject obj) { 436 return null; 437 } 438 439 @Override 440 public int hashCode() { 441 final Class<?> type = getCurrentType(); 442 return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (type == null ? 0 : type.hashCode()); 443 } 444 445 @Override 446 public boolean equals(final Object other) { 447 if (this == other) { 448 return true; 449 } 450 451 if (other == null || this.getClass() != other.getClass()) { 452 return false; 453 } 454 455 final Property otherProperty = (Property)other; 456 457 return getFlags() == otherProperty.getFlags() && 458 getSlot() == otherProperty.getSlot() && 459 getCurrentType() == otherProperty.getCurrentType() && 460 getKey().equals(otherProperty.getKey()); 461 } 462 463 @Override 464 public String toString() { 465 final StringBuilder sb = new StringBuilder(); 466 final Class<?> type = getCurrentType(); 467 468 sb.append(getKey()). 469 append("(0x"). 470 append(Integer.toHexString(flags)). 471 append(") "). 472 append(getClass().getSimpleName()). 473 append(" {"). 474 append(type == null ? "UNDEFINED" : Type.typeFor(type).getDescriptor()). 475 append('}'); 476 477 if (slot != -1) { 478 sb.append('['); 479 sb.append(slot); 480 sb.append(']'); 481 } 482 483 return sb.toString(); 484 } 485 486 /** 487 * Get the current type of this field. If you are not running with dual fields enabled, 488 * this will always be Object.class. See the value representation explanation in 489 * {@link Property#getSetter(Class, PropertyMap)} and {@link ObjectClassGenerator} 490 * for more information. 491 * 492 * @return current type of property, null means undefined 493 */ 494 public Class<?> getCurrentType() { 495 return Object.class; 496 } 497 498 /** 499 * Check whether this Property can ever change its type. The default is false, and if 500 * you are not running with dual fields, the type is always object and can never change 501 * @return true if this property can change types 502 */ 503 public boolean canChangeType() { 504 return false; 505 } 506 507 /** 508 * Check whether this Property is ever used as anything but an Object. If this is used only 509 * as an object, dual fields mode need not even try to represent it as a primitive at any 510 * callsite, saving map rewrites for performance. 511 * 512 * @return true if representation should always be an object field 513 */ 514 public boolean isAlwaysObject() { 515 return (flags & IS_ALWAYS_OBJECT) == IS_ALWAYS_OBJECT; 516 } 517 518 /** 519 * Check whether this property can be primitive. This is a conservative 520 * analysis result, so {@code false} might mean that it can still be 521 * primitive 522 * 523 * @return can be primitive status 524 */ 525 public boolean canBePrimitive() { 526 return (flags & CAN_BE_PRIMITIVE) == CAN_BE_PRIMITIVE; 527 } 528 529 /** 530 * Check whether this property can be primitive. This is a conservative 531 * analysis result, so {@code true} might mean that it can still be 532 * defined, but it will never say that a property can not be undefined 533 * if it can 534 * 535 * @return can be undefined status 536 */ 537 public boolean canBeUndefined() { 538 return (flags & CAN_BE_UNDEFINED) == CAN_BE_UNDEFINED; 539 } 540 541 /** 542 * Check whether this property represents a function declaration. 543 * @return whether this property is a function declaration or not. 544 */ 545 public boolean isFunctionDeclaration() { 546 return (flags & IS_FUNCTION_DECLARATION) == IS_FUNCTION_DECLARATION; 547 } 548 } --- EOF ---