|
1 | 1 | package org.mozilla.javascript;
|
2 | 2 |
|
3 | 3 | import java.util.ArrayList;
|
| 4 | +import java.util.Arrays; |
4 | 5 | import java.util.LinkedHashMap;
|
5 | 6 | import java.util.List;
|
6 | 7 | import java.util.Map;
|
| 8 | +import java.util.Objects; |
| 9 | +import java.util.function.Predicate; |
7 | 10 |
|
8 | 11 | /**
|
9 | 12 | * Abstract Object Operations as defined by EcmaScript
|
@@ -134,8 +137,9 @@ static boolean setIntegrityLevel(Context cx, Object o, INTEGRITY_LEVEL level) {
|
134 | 137 | */
|
135 | 138 | ScriptableObject obj = ScriptableObject.ensureScriptableObject(o);
|
136 | 139 |
|
137 |
| - // TODO check .preventExtensions() return value once implemented and act accordingly to spec |
138 |
| - obj.preventExtensions(); |
| 140 | + if (!obj.preventExtensions()) { |
| 141 | + return false; |
| 142 | + } |
139 | 143 |
|
140 | 144 | for (Object key : obj.getIds(true, true)) {
|
141 | 145 | ScriptableObject desc = obj.getOwnPropertyDescriptor(cx, key);
|
@@ -303,4 +307,161 @@ static Map<Object, List<Object>> groupBy(
|
303 | 307 |
|
304 | 308 | return groups;
|
305 | 309 | }
|
| 310 | + |
| 311 | + /** |
| 312 | + * CreateListFromArrayLike ( obj [ , elementTypes ] ) |
| 313 | + * |
| 314 | + * <p>https://262.ecma-international.org/12.0/#sec-createlistfromarraylike |
| 315 | + */ |
| 316 | + static List<Object> createListFromArrayLike( |
| 317 | + Context cx, Scriptable o, Predicate<Object> elementTypesPredicate, String msg) { |
| 318 | + ScriptableObject obj = ScriptableObject.ensureScriptableObject(o); |
| 319 | + if (obj instanceof NativeArray) { |
| 320 | + Object[] arr = ((NativeArray) obj).toArray(); |
| 321 | + for (Object next : arr) { |
| 322 | + if (!elementTypesPredicate.test(next)) { |
| 323 | + throw ScriptRuntime.typeError(msg); |
| 324 | + } |
| 325 | + } |
| 326 | + return Arrays.asList(arr); |
| 327 | + } |
| 328 | + |
| 329 | + long len = lengthOfArrayLike(cx, obj); |
| 330 | + List<Object> list = new ArrayList<>(); |
| 331 | + long index = 0; |
| 332 | + while (index < len) { |
| 333 | + // String indexName = ScriptRuntime.toString(index); |
| 334 | + Object next = ScriptableObject.getProperty(obj, (int) index); |
| 335 | + if (!elementTypesPredicate.test(next)) { |
| 336 | + throw ScriptRuntime.typeError(msg); |
| 337 | + } |
| 338 | + list.add(next); |
| 339 | + index++; |
| 340 | + } |
| 341 | + return list; |
| 342 | + } |
| 343 | + |
| 344 | + /** |
| 345 | + * LengthOfArrayLike ( obj ) |
| 346 | + * |
| 347 | + * <p>https://262.ecma-international.org/12.0/#sec-lengthofarraylike |
| 348 | + */ |
| 349 | + static long lengthOfArrayLike(Context cx, Scriptable o) { |
| 350 | + Object value = ScriptableObject.getProperty(o, "length"); |
| 351 | + long len = ScriptRuntime.toLength(new Object[] {value}, 0); |
| 352 | + return len; |
| 353 | + } |
| 354 | + |
| 355 | + /** |
| 356 | + * IsCompatiblePropertyDescriptor ( Extensible, Desc, Current ) |
| 357 | + * |
| 358 | + * <p>https://262.ecma-international.org/12.0/#sec-iscompatiblepropertydescriptor |
| 359 | + */ |
| 360 | + static boolean isCompatiblePropertyDescriptor( |
| 361 | + boolean extensible, ScriptableObject desc, ScriptableObject current) { |
| 362 | + return validateAndApplyPropertyDescriptor( |
| 363 | + Undefined.SCRIPTABLE_UNDEFINED, |
| 364 | + Undefined.SCRIPTABLE_UNDEFINED, |
| 365 | + extensible, |
| 366 | + desc, |
| 367 | + current); |
| 368 | + } |
| 369 | + |
| 370 | + /** |
| 371 | + * ValidateAndApplyPropertyDescriptor ( O, P, extensible, Desc, current ) |
| 372 | + * |
| 373 | + * <p>https://262.ecma-international.org/12.0/#sec-validateandapplypropertydescriptor |
| 374 | + */ |
| 375 | + static boolean validateAndApplyPropertyDescriptor( |
| 376 | + Scriptable o, |
| 377 | + Scriptable p, |
| 378 | + boolean extensible, |
| 379 | + ScriptableObject desc, |
| 380 | + ScriptableObject current) { |
| 381 | + if (Undefined.isUndefined(current)) { |
| 382 | + if (!extensible) { |
| 383 | + return false; |
| 384 | + } |
| 385 | + |
| 386 | + if (ScriptableObject.isGenericDescriptor(desc) |
| 387 | + || ScriptableObject.isDataDescriptor(desc)) { |
| 388 | + /* |
| 389 | + i. i. If O is not undefined, create an own data property named P of object O whose [[Value]], [[Writable]], [[Enumerable]], and [[Configurable]] attribute values are described by Desc. |
| 390 | + If the value of an attribute field of Desc is absent, the attribute of the newly created property is set to its default value. |
| 391 | + */ |
| 392 | + } else { |
| 393 | + /* |
| 394 | + ii. ii. If O is not undefined, create an own accessor property named P of object O whose [[Get]], [[Set]], [[Enumerable]], and [[Configurable]] attribute values are described by Desc. If the value of an attribute field of Desc is absent, the attribute of the newly created property is set to its default value. |
| 395 | + */ |
| 396 | + } |
| 397 | + return true; |
| 398 | + } |
| 399 | + |
| 400 | + if (desc.getIds().length == 0) { |
| 401 | + return true; |
| 402 | + } |
| 403 | + |
| 404 | + if (Boolean.FALSE.equals(current.get("configurable"))) { |
| 405 | + if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "configurable")) |
| 406 | + && Boolean.TRUE.equals(desc.get("configurable"))) { |
| 407 | + return false; |
| 408 | + } |
| 409 | + |
| 410 | + if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "enumerable")) |
| 411 | + && !Objects.equals(desc.get("enumerable"), current.get("enumerable"))) { |
| 412 | + return false; |
| 413 | + } |
| 414 | + } |
| 415 | + |
| 416 | + // if (!ScriptableObject.isGenericDescriptor(desc)) { |
| 417 | + if (ScriptableObject.isGenericDescriptor(desc)) { |
| 418 | + return true; |
| 419 | + } else if (!Objects.equals( |
| 420 | + ScriptableObject.isGenericDescriptor(current), |
| 421 | + ScriptableObject.isGenericDescriptor(desc))) { |
| 422 | + if (Boolean.FALSE.equals(current.get("configurable"))) { |
| 423 | + return false; |
| 424 | + } |
| 425 | + if (ScriptableObject.isDataDescriptor(current)) { |
| 426 | + if (Boolean.FALSE.equals(current.get("configurable"))) { |
| 427 | + // i. i. If O is not undefined, convert the property named P of object O from a |
| 428 | + // data property to an accessor property. Preserve the existing values of the |
| 429 | + // converted property's [[Configurable]] and [[Enumerable]] attributes and set |
| 430 | + // the rest of the property's attributes to their default values. |
| 431 | + } else { |
| 432 | + // i. i. If O is not undefined, convert the property named P of object O from an |
| 433 | + // accessor property to a data property. Preserve the existing values of the |
| 434 | + // converted property's [[Configurable]] and [[Enumerable]] attributes and set |
| 435 | + // the rest of the property's attributes to their default values. |
| 436 | + } |
| 437 | + } |
| 438 | + } else if (ScriptableObject.isDataDescriptor(current) |
| 439 | + && ScriptableObject.isDataDescriptor(desc)) { |
| 440 | + if (Boolean.FALSE.equals(current.get("configurable")) |
| 441 | + && Boolean.FALSE.equals(current.get("writable"))) { |
| 442 | + if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "writable")) |
| 443 | + && Boolean.TRUE.equals(desc.get("writable"))) { |
| 444 | + return false; |
| 445 | + } |
| 446 | + if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "value")) |
| 447 | + && !Objects.equals(desc.get("value"), current.get("value"))) { |
| 448 | + return false; |
| 449 | + } |
| 450 | + return true; |
| 451 | + } |
| 452 | + } else { |
| 453 | + if (Boolean.FALSE.equals(current.get("configurable"))) { |
| 454 | + if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "set")) |
| 455 | + && !Objects.equals(desc.get("set"), current.get("set"))) { |
| 456 | + return false; |
| 457 | + } |
| 458 | + if (Boolean.TRUE.equals(ScriptableObject.hasProperty(desc, "get")) |
| 459 | + && !Objects.equals(desc.get("get"), current.get("get"))) { |
| 460 | + return false; |
| 461 | + } |
| 462 | + return true; |
| 463 | + } |
| 464 | + } |
| 465 | + return true; |
| 466 | + } |
306 | 467 | }
|
0 commit comments