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() {
|