|
1 | 1 | /* |
2 | | - * Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | 4 | * |
5 | 5 | * The Universal Permissive License (UPL), Version 1.0 |
|
41 | 41 | package com.oracle.truffle.trufflenode.node; |
42 | 42 |
|
43 | 43 | import java.util.ArrayList; |
44 | | -import java.util.Arrays; |
45 | 44 | import java.util.List; |
46 | 45 |
|
| 46 | +import org.graalvm.collections.EconomicSet; |
| 47 | + |
47 | 48 | import com.oracle.truffle.api.CompilerDirectives; |
48 | 49 | import com.oracle.truffle.api.frame.VirtualFrame; |
49 | 50 | import com.oracle.truffle.api.object.HiddenKey; |
|
55 | 56 | import com.oracle.truffle.js.runtime.JSRuntime; |
56 | 57 | import com.oracle.truffle.js.runtime.JavaScriptRootNode; |
57 | 58 | import com.oracle.truffle.js.runtime.Symbol; |
| 59 | +import com.oracle.truffle.js.runtime.array.ScriptArray; |
58 | 60 | import com.oracle.truffle.js.runtime.builtins.JSAbstractArray; |
59 | 61 | import com.oracle.truffle.js.runtime.builtins.JSArray; |
| 62 | +import com.oracle.truffle.js.runtime.builtins.JSArrayObject; |
60 | 63 | import com.oracle.truffle.js.runtime.objects.JSDynamicObject; |
61 | 64 | import com.oracle.truffle.js.runtime.objects.JSObject; |
62 | 65 | import com.oracle.truffle.js.runtime.objects.PropertyDescriptor; |
@@ -346,45 +349,47 @@ private Object executeOwnKeys(ObjectTemplate template, Object holder, Object[] a |
346 | 349 | PropertyHandler indexedHandler = template.getIndexedPropertyHandler(); |
347 | 350 | PropertyHandler namedHandler = template.getNamedPropertyHandler(); |
348 | 351 | JSDynamicObject proxy = (JSDynamicObject) holder; |
| 352 | + JSDynamicObject target = (JSDynamicObject) arguments[2]; |
349 | 353 |
|
350 | | - Object[] nativeCallArgs = JSArguments.create(proxy, arguments[1], arguments[2]); |
351 | | - JSDynamicObject ownKeys = null; |
352 | | - if (namedHandler != null && namedHandler.getEnumerator() != 0) { |
353 | | - ownKeys = (JSDynamicObject) NativeAccess.executePropertyHandlerEnumerator(namedHandler.getEnumerator(), holder, nativeCallArgs, namedHandler.getData()); |
354 | | - } |
| 354 | + Object[] nativeCallArgs = JSArguments.create(proxy, arguments[1], target); |
| 355 | + List<Object> ownKeys = new ArrayList<>(); |
| 356 | + EconomicSet<Object> ownKeysSet = EconomicSet.create(); |
| 357 | + |
| 358 | + // Note: the indexed and the named handler may both return the same result. |
| 359 | + // This is currently the case for the global object template of ContextifyContext. |
355 | 360 | if (indexedHandler != null && indexedHandler.getEnumerator() != 0) { |
356 | | - JSDynamicObject ownKeys2 = (JSDynamicObject) NativeAccess.executePropertyHandlerEnumerator(indexedHandler.getEnumerator(), holder, nativeCallArgs, indexedHandler.getData()); |
357 | | - if (JSArray.isJSArray(ownKeys2)) { |
358 | | - // indexed handler returns array of numbers but we need an array of property keys |
359 | | - long length = JSAbstractArray.arrayGetLength(ownKeys2); |
360 | | - for (long i = 0; i < length; i++) { |
361 | | - Object key = JSObject.get(ownKeys2, i); |
362 | | - JSObject.set(ownKeys2, i, JSRuntime.toString(key)); |
363 | | - } |
364 | | - ownKeys = concatArrays(ownKeys, ownKeys2); |
| 361 | + Object handlerResult = NativeAccess.executePropertyHandlerEnumerator(indexedHandler.getEnumerator(), holder, nativeCallArgs, indexedHandler.getData()); |
| 362 | + if (handlerResult instanceof JSArrayObject resultArray) { |
| 363 | + addKeysFromHandlerResultArray(resultArray, ownKeys, ownKeysSet); |
365 | 364 | } |
366 | 365 | } |
367 | | - if (!JSArray.isJSArray(ownKeys)) { |
368 | | - ownKeys = JSArray.createEmpty(context, getRealm(), 0); |
| 366 | + |
| 367 | + if (namedHandler != null && namedHandler.getEnumerator() != 0) { |
| 368 | + Object handlerResult = NativeAccess.executePropertyHandlerEnumerator(namedHandler.getEnumerator(), holder, nativeCallArgs, namedHandler.getData()); |
| 369 | + if (handlerResult instanceof JSArrayObject resultArray) { |
| 370 | + addKeysFromHandlerResultArray(resultArray, ownKeys, ownKeysSet); |
| 371 | + } |
369 | 372 | } |
370 | | - return ownKeys; |
| 373 | + |
| 374 | + List<Object> targetList = JSObject.ownPropertyKeys(target); |
| 375 | + for (Object propertyKey : targetList) { |
| 376 | + if (ownKeysSet.add(propertyKey)) { |
| 377 | + ownKeys.add(propertyKey); |
| 378 | + } |
| 379 | + } |
| 380 | + |
| 381 | + return JSArray.createConstant(context, getRealm(), ownKeys.toArray(ScriptArray.EMPTY_OBJECT_ARRAY)); |
371 | 382 | } |
372 | 383 |
|
373 | | - private JSDynamicObject concatArrays(JSDynamicObject array1, JSDynamicObject array2) { |
374 | | - if (JSArray.isJSArray(array1)) { |
375 | | - if (JSArray.isJSArray(array2)) { |
376 | | - List<Object> keys = new ArrayList<>(Arrays.asList(JSArray.toArray(array1))); |
377 | | - for (Object key : JSArray.toArray(array2)) { |
378 | | - if (!keys.contains(key)) { |
379 | | - keys.add(key); |
380 | | - } |
381 | | - } |
382 | | - return JSArray.createConstant(context, getRealm(), keys.toArray()); |
383 | | - } else { |
384 | | - return array1; |
| 384 | + private static void addKeysFromHandlerResultArray(JSArrayObject resultArray, List<Object> keys, EconomicSet<Object> keysSet) { |
| 385 | + long length = JSAbstractArray.arrayGetLength(resultArray); |
| 386 | + for (long i = 0; i < length; i++) { |
| 387 | + Object key = JSObject.get(resultArray, i); |
| 388 | + // handler returns numeric keys as numbers but we need an array of property keys |
| 389 | + Object propertyKey = JSRuntime.isPropertyKey(key) ? key : JSRuntime.toString(key); |
| 390 | + if (keysSet.add(propertyKey)) { |
| 391 | + keys.add(propertyKey); |
385 | 392 | } |
386 | | - } else { |
387 | | - return array2; |
388 | 393 | } |
389 | 394 | } |
390 | 395 |
|
|
0 commit comments