src/jdk/nashorn/internal/runtime/ScriptObject.java

Print this page




 308      * @return true if this has a {@link DataPropertyDescriptor}, i.e. the object has a property value and is writable
 309      */
 310     public final boolean isDataDescriptor() {
 311         return has(VALUE) || has(WRITABLE);
 312     }
 313 
 314     /**
 315      * ECMA 8.10.3 IsGenericDescriptor ( Desc )
 316      * @return true if this has a descriptor describing an {@link AccessorPropertyDescriptor} or {@link DataPropertyDescriptor}
 317      */
 318     public final boolean isGenericDescriptor() {
 319         return isAccessorDescriptor() || isDataDescriptor();
 320     }
 321 
 322     /**
 323       * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
 324       *
 325       * @return property descriptor
 326       */
 327     public final PropertyDescriptor toPropertyDescriptor() {
 328         final GlobalObject global = (GlobalObject) Context.getGlobalTrusted();
 329 
 330         final PropertyDescriptor desc;
 331         if (isDataDescriptor()) {
 332             if (has(SET) || has(GET)) {
 333                 throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
 334             }
 335 
 336             desc = global.newDataDescriptor(UNDEFINED, false, false, false);
 337         } else if (isAccessorDescriptor()) {
 338             if (has(VALUE) || has(WRITABLE)) {
 339                 throw typeError((ScriptObject)global, "inconsistent.property.descriptor");
 340             }
 341 
 342             desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false);
 343         } else {
 344             desc = global.newGenericDescriptor(false, false);
 345         }
 346 
 347         return desc.fillFrom(this);
 348     }
 349 
 350     /**
 351      * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
 352      *
 353      * @param global  global scope object
 354      * @param obj object to create property descriptor from
 355      *
 356      * @return property descriptor
 357      */
 358     public static PropertyDescriptor toPropertyDescriptor(final ScriptObject global, final Object obj) {
 359         if (obj instanceof ScriptObject) {
 360             return ((ScriptObject)obj).toPropertyDescriptor();
 361         }
 362 
 363         throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj));
 364     }
 365 
 366     /**
 367      * ECMA 8.12.1 [[GetOwnProperty]] (P)
 368      *
 369      * @param key property key
 370      *
 371      * @return Returns the Property Descriptor of the named own property of this
 372      * object, or undefined if absent.
 373      */
 374     public Object getOwnPropertyDescriptor(final String key) {
 375         final Property property = getMap().findProperty(key);
 376 
 377         final GlobalObject global = (GlobalObject)Context.getGlobalTrusted();
 378 
 379         if (property != null) {
 380             final ScriptFunction get   = property.getGetterFunction(this);
 381             final ScriptFunction set   = property.getSetterFunction(this);
 382 
 383             final boolean configurable = property.isConfigurable();
 384             final boolean enumerable   = property.isEnumerable();
 385             final boolean writable     = property.isWritable();
 386 
 387             if (property instanceof UserAccessorProperty) {
 388                 return global.newAccessorDescriptor(
 389                     (get != null) ?
 390                         get :
 391                         UNDEFINED,
 392                     (set != null) ?
 393                         set :
 394                         UNDEFINED,
 395                     configurable,
 396                     enumerable);
 397             }


 422 
 423         if (res != UNDEFINED) {
 424             return res;
 425         } else if (getProto() != null) {
 426             return getProto().getOwnPropertyDescriptor(key);
 427         } else {
 428             return UNDEFINED;
 429         }
 430     }
 431 
 432     /**
 433      * ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw)
 434      *
 435      * @param key the property key
 436      * @param propertyDesc the property descriptor
 437      * @param reject is the property extensible - true means new definitions are rejected
 438      *
 439      * @return true if property was successfully defined
 440      */
 441     public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
 442         final ScriptObject       global  = Context.getGlobalTrusted();
 443         final PropertyDescriptor desc    = toPropertyDescriptor(global, propertyDesc);
 444         final Object             current = getOwnPropertyDescriptor(key);
 445         final String             name    = JSType.toString(key);
 446 
 447         if (current == UNDEFINED) {
 448             if (isExtensible()) {
 449                 // add a new own property
 450                 addOwnProperty(key, desc);
 451                 return true;
 452             }
 453             // new property added to non-extensible object
 454             if (reject) {
 455                 throw typeError(global, "object.non.extensible", name, ScriptRuntime.safeToString(this));
 456             }
 457             return false;
 458         }
 459         // modifying an existing property
 460         final PropertyDescriptor currentDesc = (PropertyDescriptor) current;
 461         final PropertyDescriptor newDesc     = desc;
 462 


 620 
 621             if (data.has(index)) {
 622                 setArray(data.delete(index));
 623             }
 624         }
 625     }
 626 
 627     /**
 628       * Add a new property to the object.
 629       *
 630       * @param key          property key
 631       * @param propertyDesc property descriptor for property
 632       */
 633     public final void addOwnProperty(final String key, final PropertyDescriptor propertyDesc) {
 634         // Already checked that there is no own property with that key.
 635         PropertyDescriptor pdesc = propertyDesc;
 636 
 637         final int propFlags = Property.toFlags(pdesc);
 638 
 639         if (pdesc.type() == PropertyDescriptor.GENERIC) {
 640             final GlobalObject global = (GlobalObject) Context.getGlobalTrusted();
 641             final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false);
 642 
 643             dDesc.fillFrom((ScriptObject)pdesc);
 644             pdesc = dDesc;
 645         }
 646 
 647         final int type = pdesc.type();
 648         if (type == PropertyDescriptor.DATA) {
 649             addOwnProperty(key, propFlags, pdesc.getValue());
 650         } else if (type == PropertyDescriptor.ACCESSOR) {
 651             addOwnProperty(key, propFlags,
 652                     pdesc.has(GET) ? pdesc.getGetter() : null,
 653                     pdesc.has(SET) ? pdesc.getSetter() : null);
 654         }
 655 
 656         checkIntegerKey(key);
 657     }
 658 
 659     /**
 660      * Low level property API (not using property descriptors)


1133     /**
1134      * Set the __proto__ of an object with checks.
1135      * @param newProto Prototype to set.
1136      */
1137     public final void setProtoCheck(final Object newProto) {
1138         if (!isExtensible()) {
1139             throw typeError("__proto__.set.non.extensible", ScriptRuntime.safeToString(this));
1140         }
1141 
1142         if (newProto == null || newProto instanceof ScriptObject) {
1143             // check for circularity
1144             ScriptObject p = (ScriptObject)newProto;
1145             while (p != null) {
1146                 if (p == this) {
1147                     throw typeError("circular.__proto__.set", ScriptRuntime.safeToString(this));
1148                 }
1149                 p = p.getProto();
1150             }
1151             setProto((ScriptObject)newProto);
1152         } else {
1153             final ScriptObject global = Context.getGlobalTrusted();
1154             final Object  newProtoObject = JSType.toScriptObject(global, newProto);
1155 
1156             if (newProtoObject instanceof ScriptObject) {
1157                 setProto((ScriptObject)newProtoObject);
1158             } else {
1159                 throw typeError(global, "cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
1160             }
1161         }
1162     }
1163 
1164     /**
1165      * return an array of own property keys associated with the object.
1166      *
1167      * @param all True if to include non-enumerable keys.
1168      * @return Array of keys.
1169      */
1170     public String[] getOwnKeys(final boolean all) {
1171         final List<Object> keys    = new ArrayList<>();
1172         final PropertyMap  selfMap = this.getMap();
1173 


1223      *
1224      * @return string description of this object, e.g. {@code [object Object]}
1225      */
1226     public String safeToString() {
1227         return "[object " + getClassName() + "]";
1228     }
1229 
1230     /**
1231      * Return the default value of the object with a given preferred type hint.
1232      * The preferred type hints are String.class for type String, Number.class
1233      * for type Number. <p>
1234      *
1235      * A <code>hint</code> of null means "no hint".
1236      *
1237      * ECMA 8.12.8 [[DefaultValue]](hint)
1238      *
1239      * @param typeHint the preferred type hint
1240      * @return the default value
1241      */
1242     public Object getDefaultValue(final Class<?> typeHint) {
1243         // We delegate to GlobalObject, as the implementation uses dynamic call sites to invoke object's "toString" and
1244         // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts
1245         // are being executed in a long-running program, we move the code and their associated dynamic call sites
1246         // (Global.TO_STRING and Global.VALUE_OF) into per-context code.
1247         return ((GlobalObject)Context.getGlobalTrusted()).getDefaultValue(this, typeHint);
1248     }
1249 
1250     /**
1251      * Checking whether a script object is an instance of another. Used
1252      * in {@link ScriptFunction} for hasInstance implementation, walks
1253      * the proto chain
1254      *
1255      * @param instance instace to check
1256      * @return true if 'instance' is an instance of this object
1257      */
1258     public boolean isInstance(final ScriptObject instance) {
1259         return false;
1260     }
1261 
1262     /**
1263      * Flag this ScriptObject as non extensible
1264      *
1265      * @return the object after being made non extensible
1266      */
1267     public ScriptObject preventExtensions() {




 308      * @return true if this has a {@link DataPropertyDescriptor}, i.e. the object has a property value and is writable
 309      */
 310     public final boolean isDataDescriptor() {
 311         return has(VALUE) || has(WRITABLE);
 312     }
 313 
 314     /**
 315      * ECMA 8.10.3 IsGenericDescriptor ( Desc )
 316      * @return true if this has a descriptor describing an {@link AccessorPropertyDescriptor} or {@link DataPropertyDescriptor}
 317      */
 318     public final boolean isGenericDescriptor() {
 319         return isAccessorDescriptor() || isDataDescriptor();
 320     }
 321 
 322     /**
 323       * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
 324       *
 325       * @return property descriptor
 326       */
 327     public final PropertyDescriptor toPropertyDescriptor() {
 328         final Global global = Context.getGlobal();
 329 
 330         final PropertyDescriptor desc;
 331         if (isDataDescriptor()) {
 332             if (has(SET) || has(GET)) {
 333                 throw typeError(global, "inconsistent.property.descriptor");
 334             }
 335 
 336             desc = global.newDataDescriptor(UNDEFINED, false, false, false);
 337         } else if (isAccessorDescriptor()) {
 338             if (has(VALUE) || has(WRITABLE)) {
 339                 throw typeError(global, "inconsistent.property.descriptor");
 340             }
 341 
 342             desc = global.newAccessorDescriptor(UNDEFINED, UNDEFINED, false, false);
 343         } else {
 344             desc = global.newGenericDescriptor(false, false);
 345         }
 346 
 347         return desc.fillFrom(this);
 348     }
 349 
 350     /**
 351      * ECMA 8.10.5 ToPropertyDescriptor ( Obj )
 352      *
 353      * @param global  global scope object
 354      * @param obj object to create property descriptor from
 355      *
 356      * @return property descriptor
 357      */
 358     public static PropertyDescriptor toPropertyDescriptor(final Global global, final Object obj) {
 359         if (obj instanceof ScriptObject) {
 360             return ((ScriptObject)obj).toPropertyDescriptor();
 361         }
 362 
 363         throw typeError(global, "not.an.object", ScriptRuntime.safeToString(obj));
 364     }
 365 
 366     /**
 367      * ECMA 8.12.1 [[GetOwnProperty]] (P)
 368      *
 369      * @param key property key
 370      *
 371      * @return Returns the Property Descriptor of the named own property of this
 372      * object, or undefined if absent.
 373      */
 374     public Object getOwnPropertyDescriptor(final String key) {
 375         final Property property = getMap().findProperty(key);
 376 
 377         final Global global = Context.getGlobal();
 378 
 379         if (property != null) {
 380             final ScriptFunction get   = property.getGetterFunction(this);
 381             final ScriptFunction set   = property.getSetterFunction(this);
 382 
 383             final boolean configurable = property.isConfigurable();
 384             final boolean enumerable   = property.isEnumerable();
 385             final boolean writable     = property.isWritable();
 386 
 387             if (property instanceof UserAccessorProperty) {
 388                 return global.newAccessorDescriptor(
 389                     (get != null) ?
 390                         get :
 391                         UNDEFINED,
 392                     (set != null) ?
 393                         set :
 394                         UNDEFINED,
 395                     configurable,
 396                     enumerable);
 397             }


 422 
 423         if (res != UNDEFINED) {
 424             return res;
 425         } else if (getProto() != null) {
 426             return getProto().getOwnPropertyDescriptor(key);
 427         } else {
 428             return UNDEFINED;
 429         }
 430     }
 431 
 432     /**
 433      * ECMA 8.12.9 [[DefineOwnProperty]] (P, Desc, Throw)
 434      *
 435      * @param key the property key
 436      * @param propertyDesc the property descriptor
 437      * @param reject is the property extensible - true means new definitions are rejected
 438      *
 439      * @return true if property was successfully defined
 440      */
 441     public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
 442         final Global             global  = Context.getGlobal();
 443         final PropertyDescriptor desc    = toPropertyDescriptor(global, propertyDesc);
 444         final Object             current = getOwnPropertyDescriptor(key);
 445         final String             name    = JSType.toString(key);
 446 
 447         if (current == UNDEFINED) {
 448             if (isExtensible()) {
 449                 // add a new own property
 450                 addOwnProperty(key, desc);
 451                 return true;
 452             }
 453             // new property added to non-extensible object
 454             if (reject) {
 455                 throw typeError(global, "object.non.extensible", name, ScriptRuntime.safeToString(this));
 456             }
 457             return false;
 458         }
 459         // modifying an existing property
 460         final PropertyDescriptor currentDesc = (PropertyDescriptor) current;
 461         final PropertyDescriptor newDesc     = desc;
 462 


 620 
 621             if (data.has(index)) {
 622                 setArray(data.delete(index));
 623             }
 624         }
 625     }
 626 
 627     /**
 628       * Add a new property to the object.
 629       *
 630       * @param key          property key
 631       * @param propertyDesc property descriptor for property
 632       */
 633     public final void addOwnProperty(final String key, final PropertyDescriptor propertyDesc) {
 634         // Already checked that there is no own property with that key.
 635         PropertyDescriptor pdesc = propertyDesc;
 636 
 637         final int propFlags = Property.toFlags(pdesc);
 638 
 639         if (pdesc.type() == PropertyDescriptor.GENERIC) {
 640             final Global global = Context.getGlobal();
 641             final PropertyDescriptor dDesc = global.newDataDescriptor(UNDEFINED, false, false, false);
 642 
 643             dDesc.fillFrom((ScriptObject)pdesc);
 644             pdesc = dDesc;
 645         }
 646 
 647         final int type = pdesc.type();
 648         if (type == PropertyDescriptor.DATA) {
 649             addOwnProperty(key, propFlags, pdesc.getValue());
 650         } else if (type == PropertyDescriptor.ACCESSOR) {
 651             addOwnProperty(key, propFlags,
 652                     pdesc.has(GET) ? pdesc.getGetter() : null,
 653                     pdesc.has(SET) ? pdesc.getSetter() : null);
 654         }
 655 
 656         checkIntegerKey(key);
 657     }
 658 
 659     /**
 660      * Low level property API (not using property descriptors)


1133     /**
1134      * Set the __proto__ of an object with checks.
1135      * @param newProto Prototype to set.
1136      */
1137     public final void setProtoCheck(final Object newProto) {
1138         if (!isExtensible()) {
1139             throw typeError("__proto__.set.non.extensible", ScriptRuntime.safeToString(this));
1140         }
1141 
1142         if (newProto == null || newProto instanceof ScriptObject) {
1143             // check for circularity
1144             ScriptObject p = (ScriptObject)newProto;
1145             while (p != null) {
1146                 if (p == this) {
1147                     throw typeError("circular.__proto__.set", ScriptRuntime.safeToString(this));
1148                 }
1149                 p = p.getProto();
1150             }
1151             setProto((ScriptObject)newProto);
1152         } else {
1153             final Global global = Context.getGlobal();
1154             final Object  newProtoObject = JSType.toScriptObject(global, newProto);
1155 
1156             if (newProtoObject instanceof ScriptObject) {
1157                 setProto((ScriptObject)newProtoObject);
1158             } else {
1159                 throw typeError(global, "cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
1160             }
1161         }
1162     }
1163 
1164     /**
1165      * return an array of own property keys associated with the object.
1166      *
1167      * @param all True if to include non-enumerable keys.
1168      * @return Array of keys.
1169      */
1170     public String[] getOwnKeys(final boolean all) {
1171         final List<Object> keys    = new ArrayList<>();
1172         final PropertyMap  selfMap = this.getMap();
1173 


1223      *
1224      * @return string description of this object, e.g. {@code [object Object]}
1225      */
1226     public String safeToString() {
1227         return "[object " + getClassName() + "]";
1228     }
1229 
1230     /**
1231      * Return the default value of the object with a given preferred type hint.
1232      * The preferred type hints are String.class for type String, Number.class
1233      * for type Number. <p>
1234      *
1235      * A <code>hint</code> of null means "no hint".
1236      *
1237      * ECMA 8.12.8 [[DefaultValue]](hint)
1238      *
1239      * @param typeHint the preferred type hint
1240      * @return the default value
1241      */
1242     public Object getDefaultValue(final Class<?> typeHint) {
1243         // We delegate to Global, as the implementation uses dynamic call sites to invoke object's "toString" and
1244         // "valueOf" methods, and in order to avoid those call sites from becoming megamorphic when multiple contexts
1245         // are being executed in a long-running program, we move the code and their associated dynamic call sites
1246         // (Global.TO_STRING and Global.VALUE_OF) into per-context code.
1247         return Context.getGlobal().getDefaultValue(this, typeHint);
1248     }
1249 
1250     /**
1251      * Checking whether a script object is an instance of another. Used
1252      * in {@link ScriptFunction} for hasInstance implementation, walks
1253      * the proto chain
1254      *
1255      * @param instance instace to check
1256      * @return true if 'instance' is an instance of this object
1257      */
1258     public boolean isInstance(final ScriptObject instance) {
1259         return false;
1260     }
1261 
1262     /**
1263      * Flag this ScriptObject as non extensible
1264      *
1265      * @return the object after being made non extensible
1266      */
1267     public ScriptObject preventExtensions() {