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.io.Serializable;
33 import java.lang.invoke.MethodHandle;
34 import java.lang.invoke.SwitchPoint;
35 import java.util.Objects;
36 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
37
38 /**
39 * This is the abstract superclass representing a JavaScript Property.
40 * The {@link PropertyMap} map links keys to properties, and consequently
41 * instances of this class make up the values in the PropertyMap
42 *
43 * @see PropertyMap
44 * @see AccessorProperty
45 * @see UserAccessorProperty
46 */
47 public abstract class Property implements Serializable {
48 /*
49 * ECMA 8.6.1 Property Attributes
50 *
51 * We use negative flags because most properties are expected to
52 * be 'writable', 'configurable' and 'enumerable'. With negative flags,
53 * we can use leave flag byte initialized with (the default) zero value.
54 */
55
56 /** Mask for property being both writable, enumerable and configurable */
57 public static final int WRITABLE_ENUMERABLE_CONFIGURABLE = 0b0000_0000_0000;
58
59 /** ECMA 8.6.1 - Is this property not writable? */
60 public static final int NOT_WRITABLE = 1 << 0;
61
62 /** ECMA 8.6.1 - Is this property not enumerable? */
63 public static final int NOT_ENUMERABLE = 1 << 1;
64
65 /** ECMA 8.6.1 - Is this property not configurable? */
66 public static final int NOT_CONFIGURABLE = 1 << 2;
67
68 private static final int MODIFY_MASK = NOT_WRITABLE | NOT_ENUMERABLE | NOT_CONFIGURABLE;
69
70 /** Is this a function parameter? */
71 public static final int IS_PARAMETER = 1 << 3;
72
73 /** Is parameter accessed thru arguments? */
74 public static final int HAS_ARGUMENTS = 1 << 4;
75
76 /** Is this a function declaration property ? */
77 public static final int IS_FUNCTION_DECLARATION = 1 << 5;
78
79 /**
80 * Is this is a primitive field given to us by Nasgen, i.e.
81 * something we can be sure remains a constant whose type
82 * is narrower than object, e.g. Math.PI which is declared
83 * as a double
84 */
85 public static final int IS_NASGEN_PRIMITIVE = 1 << 6;
86
87 /** Is this property bound to a receiver? This means get/set operations will be delegated to
88 * a statically defined object instead of the object passed as callsite parameter. */
89 public static final int IS_BOUND = 1 << 7;
90
91 /** Is this a lexically scoped LET or CONST variable that is dead until it is declared. */
92 public static final int NEEDS_DECLARATION = 1 << 8;
93
94 /** Property key. */
95 private final String key;
96
97 /** Property flags. */
98 private int flags;
99
100 /** Property field number or spill slot. */
101 private final int slot;
102
103 /** SwitchPoint that is invalidated when property is changed, optional */
104 protected SwitchPoint changeCallback;
105
106 private static final long serialVersionUID = 2099814273074501176L;
107
108 /**
109 * Constructor
110 *
111 * @param key property key
112 * @param flags property flags
113 * @param slot property field number or spill slot
114 */
115 Property(final String key, final int flags, final int slot) {
116 assert key != null;
117 this.key = key;
118 this.flags = flags;
119 this.slot = slot;
120 }
121
122 /**
123 * Copy constructor
124 *
125 * @param property source property
126 */
127 Property(final Property property, final int flags) {
128 this.key = property.key;
129 this.slot = property.slot;
130 this.changeCallback = property.changeCallback;
131 this.flags = flags;
132 }
133
134 /**
135 * Copy function
136 *
137 * @return cloned property
138 */
139 public abstract Property copy();
140
141 /**
142 * Copy function
143 *
144 * @param newType new type
145 * @return cloned property with new type
146 */
147 public abstract Property copy(final Class<?> newType);
148
149 /**
150 * Property flag utility method for {@link PropertyDescriptor}s. Given two property descriptors,
151 * return the result of merging their flags.
152 *
153 * @param oldDesc first property descriptor
154 * @param newDesc second property descriptor
155 * @return merged flags.
156 */
157 static int mergeFlags(final PropertyDescriptor oldDesc, final PropertyDescriptor newDesc) {
158 int propFlags = 0;
159 boolean value;
160
161 value = newDesc.has(CONFIGURABLE) ? newDesc.isConfigurable() : oldDesc.isConfigurable();
162 if (!value) {
163 propFlags |= NOT_CONFIGURABLE;
164 }
165
166 value = newDesc.has(ENUMERABLE) ? newDesc.isEnumerable() : oldDesc.isEnumerable();
167 if (!value) {
168 propFlags |= NOT_ENUMERABLE;
169 }
170
171 value = newDesc.has(WRITABLE) ? newDesc.isWritable() : oldDesc.isWritable();
172 if (!value) {
173 propFlags |= NOT_WRITABLE;
174 }
175
176 return propFlags;
177 }
178
179 /**
180 * Set the change callback for this property, i.e. a SwitchPoint
181 * that will be invalidated when the value of the property is
182 * changed
183 * @param sp SwitchPoint to use for change callback
184 */
185 public final void setChangeCallback(final SwitchPoint sp) {
186 this.changeCallback = sp;
187 }
188
189 /**
190 * Property flag utility method for {@link PropertyDescriptor}. Get the property flags
191 * conforming to any Property using this PropertyDescriptor
192 *
193 * @param desc property descriptor
194 * @return flags for properties that conform to property descriptor
195 */
196 static int toFlags(final PropertyDescriptor desc) {
197 int propFlags = 0;
198
199 if (!desc.isConfigurable()) {
200 propFlags |= NOT_CONFIGURABLE;
201 }
202 if (!desc.isEnumerable()) {
203 propFlags |= NOT_ENUMERABLE;
204 }
205 if (!desc.isWritable()) {
206 propFlags |= NOT_WRITABLE;
207 }
208
209 return propFlags;
210 }
211
212 /**
213 * Check whether this property has a user defined getter function. See {@link UserAccessorProperty}
214 * @param obj object containing getter
215 * @return true if getter function exists, false is default
216 */
217 public boolean hasGetterFunction(final ScriptObject obj) {
218 return false;
219 }
220
221 /**
222 * Check whether this property has a user defined setter function. See {@link UserAccessorProperty}
223 * @param obj object containing setter
224 * @return true if getter function exists, false is default
225 */
226 public boolean hasSetterFunction(final ScriptObject obj) {
227 return false;
228 }
229
230 /**
231 * Check whether this property is writable (see ECMA 8.6.1)
232 * @return true if writable
233 */
234 public boolean isWritable() {
235 return (flags & NOT_WRITABLE) == 0;
236 }
237
238 /**
239 * Check whether this property is writable (see ECMA 8.6.1)
240 * @return true if configurable
241 */
242 public boolean isConfigurable() {
243 return (flags & NOT_CONFIGURABLE) == 0;
244 }
245
246 /**
247 * Check whether this property is enumerable (see ECMA 8.6.1)
248 * @return true if enumerable
249 */
250 public boolean isEnumerable() {
251 return (flags & NOT_ENUMERABLE) == 0;
252 }
253
254 /**
255 * Check whether this property is used as a function parameter
256 * @return true if parameter
257 */
258 public boolean isParameter() {
259 return (flags & IS_PARAMETER) == IS_PARAMETER;
260 }
261
262 /**
263 * Check whether this property is in an object with arguments field
264 * @return true if has arguments
265 */
266 public boolean hasArguments() {
267 return (flags & HAS_ARGUMENTS) == HAS_ARGUMENTS;
268 }
269
270 /**
271 * Check whether this is a spill property, i.e. one that will not
272 * be stored in a specially generated field in the property class.
273 * The spill pool is maintained separately, as a growing Object array
274 * in the {@link ScriptObject}.
275 *
276 * @return true if spill property
277 */
278 public boolean isSpill() {
279 return false;
280 }
281
282 /**
283 * Is this property bound to a receiver? If this method returns {@code true} get and set operations
284 * will be delegated to a statically bound object instead of the object passed as parameter.
285 *
286 * @return true if this is a bound property
287 */
288 public boolean isBound() {
289 return (flags & IS_BOUND) == IS_BOUND;
290 }
291
292 /**
293 * Is this a LET or CONST property that needs to see its declaration before being usable?
294 *
295 * @return true if this is a block-scoped variable
296 */
297 public boolean needsDeclaration() {
298 return (flags & NEEDS_DECLARATION) == NEEDS_DECLARATION;
299 }
300
301 /**
302 * Add more property flags to the property. Properties are immutable here,
303 * so any property change that results in a larger flag set results in the
304 * property being cloned. Use only the return value
305 *
306 * @param propertyFlags flags to be OR:ed to the existing property flags
307 * @return new property if property set was changed, {@code this} otherwise
308 */
309 public Property addFlags(final int propertyFlags) {
310 if ((this.flags & propertyFlags) != propertyFlags) {
311 final Property cloned = this.copy();
312 cloned.flags |= propertyFlags;
313 return cloned;
314 }
315 return this;
316 }
317
318 /**
319 * Check if a flag is set for a property
320 * @param property property
321 * @param flag flag to check
322 * @return true if flag is set
323 */
324 public static boolean checkFlag(final Property property, final int flag) {
325 return (property.getFlags() & flag) == flag;
326 }
327
328 /**
329 * Get the flags for this property
330 * @return property flags
331 */
332 public int getFlags() {
333 return flags;
334 }
335
336 /**
337 * Get the modify flags for this property. The modify flags are the ECMA 8.6.1
338 * flags that decide if the Property is writable, configurable and/or enumerable.
339 *
340 * @return modify flags for property
341 */
342 public int getModifyFlags() {
343 return flags & MODIFY_MASK;
344 }
345
346 /**
347 * Remove property flags from the property. Properties are immutable here,
348 * so any property change that results in a smaller flag set results in the
349 * property being cloned. Use only the return value
350 *
351 * @param propertyFlags flags to be subtracted from the existing property flags
352 * @return new property if property set was changed, {@code this} otherwise
353 */
354 public Property removeFlags(final int propertyFlags) {
355 if ((this.flags & propertyFlags) != 0) {
356 final Property cloned = this.copy();
357 cloned.flags &= ~propertyFlags;
358 return cloned;
359 }
360 return this;
361 }
362
363 /**
364 * Reset the property for this property. Properties are immutable here,
365 * so any property change that results in a different flag sets results in the
366 * property being cloned. Use only the return value
367 *
368 * @param propertyFlags flags that are replacing from the existing property flags
369 * @return new property if property set was changed, {@code this} otherwise
370 */
371 public Property setFlags(final int propertyFlags) {
372 if (this.flags != propertyFlags) {
373 final Property cloned = this.copy();
374 cloned.flags &= ~MODIFY_MASK;
375 cloned.flags |= propertyFlags & MODIFY_MASK;
376 return cloned;
377 }
378 return this;
379 }
380
381 /**
382 * Abstract method for retrieving the getter for the property. We do not know
383 * anything about the internal representation when we request the getter, we only
384 * know that the getter will return the property as the given type.
385 *
386 * @param type getter return value type
387 * @return a getter for this property as {@code type}
388 */
389 public abstract MethodHandle getGetter(final Class<?> type);
390
391 /**
392 * Get an optimistic getter that throws an exception if type is not the known given one
393 * @param type type
394 * @param programPoint program point
395 * @return getter
396 */
397 public abstract MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint);
398
399 /**
400 * Hook to initialize method handles after deserialization.
401 *
402 * @param structure the structure class
403 */
404 abstract void initMethodHandles(final Class<?> structure);
405
406 /**
407 * Get the key for this property. This key is an ordinary string. The "name".
408 * @return key for property
409 */
410 public String getKey() {
411 return key;
412 }
413
414 /**
415 * Get the field number or spill slot
416 * @return number/slot, -1 if none exists
417 */
418 public int getSlot() {
419 return slot;
420 }
421
422 /**
423 * get the Object value of this property from {@code owner}. This allows to bypass creation of the
424 * getter MethodHandle for spill and user accessor properties.
425 *
426 * @param self the this object
427 * @param owner the owner of the property
428 * @return the property value
429 */
430 public abstract int getIntValue(final ScriptObject self, final ScriptObject owner);
431
432 /**
433 * get the Object value of this property from {@code owner}. This allows to bypass creation of the
434 * getter MethodHandle for spill and user accessor properties.
435 *
436 * @param self the this object
437 * @param owner the owner of the property
438 * @return the property value
439 */
440 public abstract long getLongValue(final ScriptObject self, final ScriptObject owner);
441
442 /**
443 * get the Object value of this property from {@code owner}. This allows to bypass creation of the
444 * getter MethodHandle for spill and user accessor properties.
445 *
446 * @param self the this object
447 * @param owner the owner of the property
448 * @return the property value
449 */
450 public abstract double getDoubleValue(final ScriptObject self, final ScriptObject owner);
451
452 /**
453 * get the Object value of this property from {@code owner}. This allows to bypass creation of the
454 * getter MethodHandle for spill and user accessor properties.
455 *
456 * @param self the this object
457 * @param owner the owner of the property
458 * @return the property value
459 */
460 public abstract Object getObjectValue(final ScriptObject self, final ScriptObject owner);
461
462 /**
463 * Set the value of this property in {@code owner}. This allows to bypass creation of the
464 * setter MethodHandle for spill and user accessor properties.
465 *
466 * @param self the this object
467 * @param owner the owner object
468 * @param value the new property value
469 * @param strict is this a strict setter?
470 */
471 public abstract void setValue(final ScriptObject self, final ScriptObject owner, final int value, final boolean strict);
472
473 /**
474 * Set the value of this property in {@code owner}. This allows to bypass creation of the
475 * setter MethodHandle for spill and user accessor properties.
476 *
477 * @param self the this object
478 * @param owner the owner object
479 * @param value the new property value
480 * @param strict is this a strict setter?
481 */
482 public abstract void setValue(final ScriptObject self, final ScriptObject owner, final long value, final boolean strict);
483
484 /**
485 * Set the value of this property in {@code owner}. This allows to bypass creation of the
486 * setter MethodHandle for spill and user accessor properties.
487 *
488 * @param self the this object
489 * @param owner the owner object
490 * @param value the new property value
491 * @param strict is this a strict setter?
492 */
493 public abstract void setValue(final ScriptObject self, final ScriptObject owner, final double value, final boolean strict);
494
495 /**
496 * Set the value of this property in {@code owner}. This allows to bypass creation of the
497 * setter MethodHandle for spill and user accessor properties.
498 *
499 * @param self the this object
500 * @param owner the owner object
501 * @param value the new property value
502 * @param strict is this a strict setter?
503 */
504 public abstract void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict);
505
506 /**
507 * Abstract method for retrieving the setter for the property. We do not know
508 * anything about the internal representation when we request the setter, we only
509 * know that the setter will take the property as a parameter of the given type.
510 * <p>
511 * Note that we have to pass the current property map from which we retrieved
512 * the property here. This is necessary for map guards if, e.g. the internal
513 * representation of the field, and consequently also the setter, changes. Then
514 * we automatically get a map guard that relinks the call site so that the
515 * older setter will never be used again.
516 * <p>
517 * see {@link ObjectClassGenerator#createSetter(Class, Class, MethodHandle, MethodHandle)}
518 * if you are interested in the internal details of this. Note that if you
519 * are running in default mode, with {@code -Dnashorn.fields.dual=true}, disabled, the setters
520 * will currently never change, as all properties are represented as Object field,
521 * the Object fields are Initialized to {@code ScriptRuntime.UNDEFINED} and primitives are
522 * boxed/unboxed upon every access, which is not necessarily optimal
523 *
524 * @param type setter parameter type
525 * @param currentMap current property map for property
526 * @return a getter for this property as {@code type}
527 */
528 public abstract MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap);
529
530 /**
531 * Get the user defined getter function if one exists. Only {@link UserAccessorProperty} instances
532 * can have user defined getters
533 * @param obj the script object
534 * @return user defined getter function, or {@code null} if none exists
535 */
536 public ScriptFunction getGetterFunction(final ScriptObject obj) {
537 return null;
538 }
539
540 /**
541 * Get the user defined setter function if one exists. Only {@link UserAccessorProperty} instances
542 * can have user defined getters
543 * @param obj the script object
544 * @return user defined getter function, or {@code null} if none exists
545 */
546 public ScriptFunction getSetterFunction(final ScriptObject obj) {
547 return null;
548 }
549
550 @Override
551 public int hashCode() {
552 final Class<?> type = getCurrentType();
553 return Objects.hashCode(this.key) ^ flags ^ getSlot() ^ (type == null ? 0 : type.hashCode());
554 }
555
556 @Override
557 public boolean equals(final Object other) {
558 if (this == other) {
559 return true;
560 }
561
562 if (other == null || this.getClass() != other.getClass()) {
563 return false;
564 }
565
566 final Property otherProperty = (Property)other;
567
568 return equalsWithoutType(otherProperty) &&
569 getCurrentType() == otherProperty.getCurrentType();
570 }
571
572 boolean equalsWithoutType(final Property otherProperty) {
573 return getFlags() == otherProperty.getFlags() &&
574 getSlot() == otherProperty.getSlot() &&
575 getKey().equals(otherProperty.getKey());
576 }
577
578 private static final String type(final Class<?> type) {
579 if (type == null) {
580 return "undef";
581 } else if (type == int.class) {
582 return "i";
583 } else if (type == long.class) {
584 return "j";
585 } else if (type == double.class) {
586 return "d";
587 } else {
588 return "o";
589 }
590 }
591
592 /**
593 * Short toString version
594 * @return short toString
595 */
596 public final String toStringShort() {
597 final StringBuilder sb = new StringBuilder();
598 final Class<?> type = getCurrentType();
599 sb.append(getKey()).append(" (").append(type(type)).append(')');
600 return sb.toString();
601 }
602
603 private static String indent(final String str, final int indent) {
604 final StringBuilder sb = new StringBuilder();
605 sb.append(str);
606 for (int i = 0; i < indent - str.length(); i++) {
607 sb.append(' ');
608 }
609 return sb.toString();
610 }
611
612 @Override
613 public String toString() {
614 final StringBuilder sb = new StringBuilder();
615 final Class<?> type = getCurrentType();
616
617 sb.append(indent(getKey(), 20)).
618 append(" id=").
619 append(Debug.id(this)).
620 append(" (0x").
621 append(indent(Integer.toHexString(flags), 4)).
622 append(") ").
623 append(getClass().getSimpleName()).
624 append(" {").
625 append(indent(type(type), 5)).
626 append('}');
627
628 if (slot != -1) {
629 sb.append(" [").
630 append("slot=").
631 append(slot).
632 append(']');
633 }
634
635 return sb.toString();
636 }
637
638 /**
639 * Get the current type of this field. If you are not running with dual fields enabled,
640 * this will always be Object.class. See the value representation explanation in
641 * {@link Property#getSetter(Class, PropertyMap)} and {@link ObjectClassGenerator}
642 * for more information.
643 *
644 * @return current type of property, null means undefined
645 */
646 public abstract Class<?> getCurrentType();
647
648 /**
649 * Reset the current type of this property
650 * @param currentType new current type
651 */
652 public abstract void setCurrentType(final Class<?> currentType);
653
654 /**
655 * Check whether this Property can ever change its type. The default is false, and if
656 * you are not running with dual fields, the type is always object and can never change
657 * @return true if this property can change types
658 */
659 public boolean canChangeType() {
660 return false;
661 }
662
663 /**
664 * Check whether this property represents a function declaration.
665 * @return whether this property is a function declaration or not.
666 */
667 public boolean isFunctionDeclaration() {
668 return (flags & IS_FUNCTION_DECLARATION) == IS_FUNCTION_DECLARATION;
669 }
670 }
--- EOF ---