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