|
40 | 40 | */ |
41 | 41 | package com.oracle.truffle.js.runtime.builtins; |
42 | 42 |
|
| 43 | +import static com.oracle.truffle.js.runtime.util.DefinePropertyUtil.nonConfigurableMessage; |
| 44 | +import static com.oracle.truffle.js.runtime.util.DefinePropertyUtil.nonWritableMessage; |
| 45 | +import static com.oracle.truffle.js.runtime.util.DefinePropertyUtil.notExtensibleMessage; |
| 46 | +import static com.oracle.truffle.js.runtime.util.DefinePropertyUtil.reject; |
| 47 | + |
43 | 48 | import java.util.AbstractMap; |
44 | 49 | import java.util.ArrayList; |
45 | 50 | import java.util.Collections; |
@@ -260,15 +265,27 @@ public PropertyDescriptor getOwnProperty(JSDynamicObject thisObj, Object key) { |
260 | 265 | @Override |
261 | 266 | public boolean defineOwnProperty(JSDynamicObject thisObj, Object key, PropertyDescriptor desc, boolean doThrow) { |
262 | 267 | assert JSRuntime.isPropertyKey(key); |
263 | | - if (!hasOwnProperty(thisObj, key) && JSObject.isExtensible(thisObj)) { |
264 | | - getHashMap(thisObj).put(key, makeFullyPopulatedPropertyDescriptor(desc)); |
265 | | - return true; |
| 268 | + PropertyDescriptor current = getHashMap(thisObj).get(key); |
| 269 | + if (current == null) { |
| 270 | + current = super.getOwnProperty(thisObj, key); |
| 271 | + boolean extensible = JSObject.isExtensible(thisObj); |
| 272 | + if (current == null) { |
| 273 | + if (!extensible) { |
| 274 | + return reject(doThrow, notExtensibleMessage(key, doThrow)); |
| 275 | + } |
| 276 | + validateAndPutDesc(thisObj, key, makeFullyPopulatedPropertyDescriptor(desc)); |
| 277 | + return true; |
| 278 | + } else { |
| 279 | + return DefinePropertyUtil.validateAndApplyPropertyDescriptor(thisObj, key, extensible, desc, current, doThrow); |
| 280 | + } |
| 281 | + } else { |
| 282 | + return validateAndApplyPropertyDescriptorExisting(thisObj, key, desc, current, doThrow); |
266 | 283 | } |
| 284 | + } |
267 | 285 |
|
268 | | - // defineProperty is currently not supported on dictionary objects, |
269 | | - // so we need to convert back to a normal shape-based object. |
270 | | - makeOrdinaryObject(thisObj, "defineOwnProperty"); |
271 | | - return super.defineOwnProperty(thisObj, key, desc, doThrow); |
| 286 | + private static PropertyDescriptor validateAndPutDesc(JSDynamicObject thisObj, Object key, PropertyDescriptor newDesc) { |
| 287 | + assert newDesc.isFullyPopulatedPropertyDescriptor(); |
| 288 | + return getHashMap(thisObj).put(key, newDesc); |
272 | 289 | } |
273 | 290 |
|
274 | 291 | /** |
@@ -296,6 +313,76 @@ private static PropertyDescriptor makeFullyPopulatedPropertyDescriptor(PropertyD |
296 | 313 | } |
297 | 314 | } |
298 | 315 |
|
| 316 | + private static boolean validateAndApplyPropertyDescriptorExisting(JSDynamicObject thisObj, Object key, PropertyDescriptor descriptor, PropertyDescriptor currentDesc, boolean doThrow) { |
| 317 | + CompilerAsserts.neverPartOfCompilation(); |
| 318 | + assert currentDesc.isFullyPopulatedPropertyDescriptor(); |
| 319 | + if (descriptor.hasNoFields()) { |
| 320 | + return true; |
| 321 | + } |
| 322 | + |
| 323 | + if (!currentDesc.getConfigurable()) { |
| 324 | + if ((descriptor.hasConfigurable() && descriptor.getConfigurable()) || |
| 325 | + (descriptor.hasEnumerable() && (descriptor.getEnumerable() != currentDesc.getEnumerable()))) { |
| 326 | + return reject(doThrow, nonConfigurableMessage(key, doThrow)); |
| 327 | + } |
| 328 | + if (!descriptor.isGenericDescriptor() && descriptor.isAccessorDescriptor() != currentDesc.isAccessorDescriptor()) { |
| 329 | + return reject(doThrow, nonConfigurableMessage(key, doThrow)); |
| 330 | + } |
| 331 | + if (currentDesc.isAccessorDescriptor()) { |
| 332 | + if ((descriptor.hasGet() && !JSRuntime.isSameValue(descriptor.getGet(), currentDesc.getGet())) || |
| 333 | + (descriptor.hasSet() && !JSRuntime.isSameValue(descriptor.getSet(), currentDesc.getSet()))) { |
| 334 | + return reject(doThrow, nonConfigurableMessage(key, doThrow)); |
| 335 | + } |
| 336 | + } else { |
| 337 | + assert currentDesc.isDataDescriptor(); |
| 338 | + if (!currentDesc.getWritable()) { |
| 339 | + if (descriptor.hasWritable() && descriptor.getWritable()) { |
| 340 | + return reject(doThrow, nonConfigurableMessage(key, doThrow)); |
| 341 | + } |
| 342 | + if (descriptor.hasValue() && !JSRuntime.isSameValue(descriptor.getValue(), currentDesc.getValue())) { |
| 343 | + return reject(doThrow, nonWritableMessage(key, doThrow)); |
| 344 | + } |
| 345 | + } |
| 346 | + } |
| 347 | + } |
| 348 | + |
| 349 | + if (currentDesc.isDataDescriptor() && descriptor.isAccessorDescriptor()) { |
| 350 | + PropertyDescriptor newDesc = PropertyDescriptor.createAccessor(descriptor.getGet(), descriptor.getSet(), |
| 351 | + descriptor.getIfHasEnumerable(currentDesc.getEnumerable()), |
| 352 | + descriptor.getIfHasConfigurable(currentDesc.getConfigurable())); |
| 353 | + validateAndPutDesc(thisObj, key, newDesc); |
| 354 | + return true; |
| 355 | + } else if (currentDesc.isAccessorDescriptor() && descriptor.isDataDescriptor()) { |
| 356 | + Object value = descriptor.hasValue() ? descriptor.getValue() : Undefined.instance; |
| 357 | + PropertyDescriptor newDesc = PropertyDescriptor.createData(value, |
| 358 | + descriptor.getIfHasEnumerable(currentDesc.getEnumerable()), |
| 359 | + descriptor.getIfHasConfigurable(currentDesc.getConfigurable()), |
| 360 | + descriptor.getWritable()); |
| 361 | + validateAndPutDesc(thisObj, key, newDesc); |
| 362 | + return true; |
| 363 | + } else { |
| 364 | + if (descriptor.hasConfigurable()) { |
| 365 | + currentDesc.setConfigurable(descriptor.getConfigurable()); |
| 366 | + } |
| 367 | + if (descriptor.hasEnumerable()) { |
| 368 | + currentDesc.setEnumerable(descriptor.getEnumerable()); |
| 369 | + } |
| 370 | + if (descriptor.hasWritable()) { |
| 371 | + currentDesc.setWritable(descriptor.getWritable()); |
| 372 | + } |
| 373 | + if (descriptor.hasValue()) { |
| 374 | + currentDesc.setValue(descriptor.getValue()); |
| 375 | + } |
| 376 | + if (descriptor.hasGet()) { |
| 377 | + currentDesc.setGet(descriptor.getGet()); |
| 378 | + } |
| 379 | + if (descriptor.hasSet()) { |
| 380 | + currentDesc.setSet(descriptor.getSet()); |
| 381 | + } |
| 382 | + return true; |
| 383 | + } |
| 384 | + } |
| 385 | + |
299 | 386 | @SuppressWarnings("unchecked") |
300 | 387 | static EconomicMap<Object, PropertyDescriptor> getHashMap(JSDynamicObject obj) { |
301 | 388 | assert JSDictionary.isJSDictionaryObject(obj); |
|
0 commit comments