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