package com.oracle.truffle.js.nodes.access;
import java.util.List;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.js.builtins.helper.ListGetNode;
import com.oracle.truffle.js.builtins.helper.ListSizeNode;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.runtime.JSConfig;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.builtins.JSArray;
import com.oracle.truffle.js.runtime.builtins.JSClass;
import com.oracle.truffle.js.runtime.builtins.JSProxy;
import com.oracle.truffle.js.runtime.objects.JSShape;
import com.oracle.truffle.js.runtime.objects.PropertyDescriptor;
import com.oracle.truffle.js.runtime.util.JSClassProfile;
import com.oracle.truffle.js.runtime.util.SimpleArrayList;
import com.oracle.truffle.js.runtime.util.UnmodifiableArrayList;
public abstract class EnumerableOwnPropertyNamesNode extends JavaScriptBaseNode {
private final boolean keys;
private final boolean values;
private final JSContext context;
@Child private JSGetOwnPropertyNode getOwnPropertyNode;
private final ConditionProfile hasFastShapesProfile = ConditionProfile.createBinaryProfile();
private final BranchProfile growProfile = BranchProfile.create();
protected EnumerableOwnPropertyNamesNode(JSContext context, boolean keys, boolean values) {
this.context = context;
this.keys = keys;
this.values = values;
}
public static EnumerableOwnPropertyNamesNode createKeys(JSContext context) {
return EnumerableOwnPropertyNamesNodeGen.create(context, true, false);
}
public static EnumerableOwnPropertyNamesNode createValues(JSContext context) {
return EnumerableOwnPropertyNamesNodeGen.create(context, false, true);
}
public static EnumerableOwnPropertyNamesNode createKeysValues(JSContext context) {
return EnumerableOwnPropertyNamesNodeGen.create(context, true, true);
}
public abstract UnmodifiableArrayList<? extends Object> execute(DynamicObject obj);
@Specialization
protected UnmodifiableArrayList<? extends Object> enumerableOwnPropertyNames(DynamicObject thisObj,
@Cached JSClassProfile jsclassProfile,
@Cached ListSizeNode listSize,
@Cached ListGetNode listGet,
@Cached HasOnlyShapePropertiesNode hasOnlyShapeProperties) {
JSClass jsclass = jsclassProfile.getJSClass(thisObj);
if (hasFastShapesProfile.profile(keys && !values && JSConfig.FastOwnKeys && hasOnlyShapeProperties.execute(thisObj, jsclass))) {
return JSShape.getEnumerablePropertyNames(thisObj.getShape());
} else {
boolean isProxy = JSProxy.isJSProxy(thisObj);
List<Object> ownKeys = jsclass.ownPropertyKeys(thisObj);
int ownKeysSize = listSize.execute(ownKeys);
SimpleArrayList<Object> properties = new SimpleArrayList<>();
for (int i = 0; i < ownKeysSize; i++) {
Object key = listGet.execute(ownKeys, i);
if (key instanceof String) {
PropertyDescriptor desc = getOwnProperty(thisObj, key);
if (desc != null && desc.getEnumerable()) {
Object element;
if (keys && !values) {
element = key;
} else {
Object value = (desc.isAccessorDescriptor() || isProxy) ? jsclass.get(thisObj, key) : desc.getValue();
if (!keys && values) {
element = value;
} else {
assert keys && values;
element = JSArray.createConstant(context, new Object[]{key, value});
}
}
properties.add(element, growProfile);
}
}
}
return new UnmodifiableArrayList<>(properties.toArray());
}
}
protected PropertyDescriptor getOwnProperty(DynamicObject thisObj, Object key) {
if (getOwnPropertyNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
getOwnPropertyNode = insert(JSGetOwnPropertyNode.create(values, true, false, false, false));
}
return getOwnPropertyNode.execute(thisObj, key);
}
}