package com.oracle.truffle.js.runtime.objects;
import static com.oracle.truffle.js.runtime.objects.JSAttributes.NOT_CONFIGURABLE;
import static com.oracle.truffle.js.runtime.objects.JSAttributes.NOT_ENUMERABLE;
import static com.oracle.truffle.js.runtime.objects.JSAttributes.NOT_WRITABLE;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.DynamicObjectLibrary;
import com.oracle.truffle.api.object.Property;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSArguments;
import com.oracle.truffle.js.runtime.JSRuntime;
public class JSProperty {
public static final int ACCESSOR = 1 << 3;
public static final int PROXY = 1 << 4;
public static final int CONST = 1 << 5;
@TruffleBoundary
public String toString(Property property) {
return "\"" + property.getKey() + "\"" + getAttributeString(property) + ":" + property.getLocation();
}
private static String getAttributeString(Property property) {
String negative = getAttributeString(property, false);
return negative.isEmpty() ? "" : ("-" + negative);
}
protected static String getAttributeString(Property property, boolean positive) {
return (isEnumerable(property) == positive ? "e" : "") + (isConfigurable(property) == positive ? "c" : "") + (isData(property) && (isWritable(property) == positive) ? "w" : "");
}
public static Object getValue(Property property, DynamicObject store, Object thisObj, Node encapsulatingNode) {
Object value = property.get(store, false);
if (isAccessor(property)) {
return getValueAccessor(thisObj, value, encapsulatingNode);
} else if (isProxy(property)) {
return ((PropertyProxy) value).get(store);
} else {
assert isData(property);
return value;
}
}
private static Object getValueAccessor(Object thisObj, Object value, Node encapsulatingNode) {
DynamicObject getter = ((Accessor) value).getGetter();
if (getter != Undefined.instance) {
return JSRuntime.call(getter, thisObj, JSArguments.EMPTY_ARGUMENTS_ARRAY, encapsulatingNode);
} else {
return Undefined.instance;
}
}
public static boolean setValue(Property property, DynamicObject store, Object thisObj, Object value, boolean isStrict, Node encapsulatingNode) {
if (isAccessor(property)) {
return setValueAccessor(property, store, thisObj, value, isStrict, encapsulatingNode);
} else {
if (isWritable(property)) {
if (isProxy(property)) {
return setValueProxy(property, store, thisObj, value, isStrict);
} else {
assert isData(property);
assert !(value instanceof Accessor || value instanceof PropertyProxy);
boolean success = DynamicObjectLibrary.getUncached().putIfPresent(store, property.getKey(), value);
assert success;
return true;
}
} else {
if (isStrict) {
throw Errors.createTypeErrorNotWritableProperty(property.getKey(), thisObj);
}
return false;
}
}
}
private static boolean setValueAccessor(Property property, DynamicObject store, Object thisObj, Object value, boolean isStrict, Node encapsulatingNode) {
DynamicObject setter = ((Accessor) JSDynamicObject.getOrNull(store, property.getKey())).getSetter();
if (setter != Undefined.instance) {
JSRuntime.call(setter, thisObj, new Object[]{value}, encapsulatingNode);
return true;
} else if (isStrict) {
throw Errors.createTypeErrorCannotSetAccessorProperty(property.getKey(), store);
} else {
return false;
}
}
private static boolean setValueProxy(Property property, DynamicObject store, Object thisObj, Object value, boolean isStrict) {
boolean ret = ((PropertyProxy) JSDynamicObject.getOrNull(store, property.getKey())).set(store, value);
if (!ret && isStrict) {
throw Errors.createTypeErrorNotWritableProperty(property.getKey(), thisObj);
}
return ret;
}
public static boolean isConfigurable(Property property) {
return (property.getFlags() & NOT_CONFIGURABLE) == 0;
}
public static boolean isEnumerable(Property property) {
return (property.getFlags() & NOT_ENUMERABLE) == 0;
}
public static boolean isWritable(Property property) {
return (property.getFlags() & NOT_WRITABLE) == 0;
}
public static boolean isProxy(Property property) {
return (property.getFlags() & PROXY) != 0;
}
public static boolean isAccessor(Property property) {
return (property.getFlags() & ACCESSOR) != 0;
}
public static boolean isData(Property property) {
return (property.getFlags() & ACCESSOR) == 0;
}
public static boolean isConst(Property property) {
return (property.getFlags() & CONST) != 0;
}
public static boolean isConfigurable(int flags) {
return (flags & NOT_CONFIGURABLE) == 0;
}
public static boolean isEnumerable(int flags) {
return (flags & NOT_ENUMERABLE) == 0;
}
public static boolean isWritable(int flags) {
return (flags & NOT_WRITABLE) == 0;
}
public static boolean isProxy(int flags) {
return (flags & PROXY) != 0;
}
public static boolean isAccessor(int flags) {
return (flags & ACCESSOR) != 0;
}
public static boolean isData(int flags) {
return (flags & ACCESSOR) == 0;
}
public static boolean isConst(int flags) {
return (flags & CONST) == 0;
}
public static PropertyProxy getConstantProxy(Property proxyProperty) {
assert isProxy(proxyProperty);
return proxyProperty.getLocation().isConstant() ? (PropertyProxy) proxyProperty.get(null, false) : null;
}
}