com.oracle.truffle.js.nodes.access.HasPropertyCacheNode Maven / Gradle / Ivy
/*
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.oracle.truffle.js.nodes.access;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.object.Location;
import com.oracle.truffle.api.object.Property;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.api.strings.TruffleString;
import com.oracle.truffle.js.nodes.module.ReadImportBindingNode;
import com.oracle.truffle.js.runtime.JSConfig;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.Strings;
import com.oracle.truffle.js.runtime.builtins.JSAdapter;
import com.oracle.truffle.js.runtime.builtins.JSModuleNamespaceObject;
import com.oracle.truffle.js.runtime.builtins.JSProxy;
import com.oracle.truffle.js.runtime.java.JavaImporter;
import com.oracle.truffle.js.runtime.java.JavaPackage;
import com.oracle.truffle.js.runtime.objects.ExportResolution;
import com.oracle.truffle.js.runtime.objects.JSDynamicObject;
import com.oracle.truffle.js.runtime.objects.JSObject;
import com.oracle.truffle.js.runtime.objects.JSProperty;
import com.oracle.truffle.js.runtime.util.JSClassProfile;
/**
* @see PropertyGetNode
*/
public class HasPropertyCacheNode extends PropertyCacheNode {
private final boolean hasOwnProperty;
private boolean propertyAssumptionCheckEnabled = true;
@Child protected HasCacheNode cacheNode;
@NeverDefault
public static HasPropertyCacheNode create(Object key, JSContext context, boolean hasOwnProperty) {
return new HasPropertyCacheNode(key, context, hasOwnProperty);
}
@NeverDefault
public static HasPropertyCacheNode create(Object key, JSContext context) {
return create(key, context, false);
}
protected HasPropertyCacheNode(Object key, JSContext context, boolean hasOwnProperty) {
super(key, context);
this.hasOwnProperty = hasOwnProperty;
}
@ExplodeLoop
public boolean hasProperty(Object thisObj) {
HasCacheNode c = cacheNode;
for (; c != null; c = c.next) {
if (c instanceof GenericHasPropertyCacheNode) {
return ((GenericHasPropertyCacheNode) c).hasProperty(thisObj, this);
}
boolean isSimpleShapeCheck = c.isSimpleShapeCheck();
ReceiverCheckNode receiverCheck = c.receiverCheck;
boolean guard;
Object castObj;
if (isSimpleShapeCheck) {
Shape shape = receiverCheck.getShape();
if (isDynamicObject(thisObj, shape)) {
JSDynamicObject jsobj = castDynamicObject(thisObj, shape);
guard = shape.check(jsobj);
castObj = jsobj;
if (!shape.getValidAssumption().isValid()) {
break;
}
} else {
continue;
}
} else {
guard = receiverCheck.accept(thisObj);
castObj = thisObj;
}
if (guard) {
if (!isSimpleShapeCheck && !receiverCheck.isValid()) {
break;
}
return c.hasProperty(castObj, this);
}
}
deoptimize(c);
return hasPropertyAndSpecialize(thisObj);
}
@TruffleBoundary
private boolean hasPropertyAndSpecialize(Object thisObj) {
HasCacheNode c = specialize(thisObj);
return c.hasProperty(thisObj, this);
}
@Override
protected HasCacheNode getCacheNode() {
return this.cacheNode;
}
@Override
protected void setCacheNode(HasCacheNode cache) {
this.cacheNode = cache;
}
public abstract static class HasCacheNode extends PropertyCacheNode.CacheNode {
@Child protected HasCacheNode next;
protected HasCacheNode(ReceiverCheckNode receiverCheck) {
super(receiverCheck);
}
@Override
protected final HasCacheNode getNext() {
return next;
}
@Override
protected final void setNext(HasCacheNode next) {
this.next = next;
}
protected abstract boolean hasProperty(Object thisObj, HasPropertyCacheNode root);
}
public abstract static class LinkedHasPropertyCacheNode extends HasCacheNode {
protected LinkedHasPropertyCacheNode(ReceiverCheckNode receiverCheckNode) {
super(receiverCheckNode);
}
}
public static final class PresentHasPropertyCacheNode extends LinkedHasPropertyCacheNode {
public PresentHasPropertyCacheNode(ReceiverCheckNode shapeCheck) {
super(shapeCheck);
}
@Override
protected boolean hasProperty(Object thisObj, HasPropertyCacheNode root) {
return true;
}
}
/**
* For use when a property is undefined. Returns undefined.
*/
public static final class AbsentHasPropertyCacheNode extends LinkedHasPropertyCacheNode {
public AbsentHasPropertyCacheNode(ReceiverCheckNode shapeCheckNode) {
super(shapeCheckNode);
}
@Override
protected boolean hasProperty(Object thisObj, HasPropertyCacheNode root) {
return false;
}
}
public static final class JSAdapterHasPropertyCacheNode extends LinkedHasPropertyCacheNode {
public JSAdapterHasPropertyCacheNode(Object key, ReceiverCheckNode receiverCheckNode) {
super(receiverCheckNode);
assert JSRuntime.isPropertyKey(key);
}
@Override
protected boolean hasProperty(Object thisObj, HasPropertyCacheNode root) {
return JSObject.hasOwnProperty((JSDynamicObject) thisObj, root.getKey());
}
}
public static final class JSProxyDispatcherPropertyHasNode extends LinkedHasPropertyCacheNode {
private final boolean hasOwnProperty;
@Child private JSProxyHasPropertyNode proxyGet;
@Child private JSGetOwnPropertyNode getOwnPropertyNode;
public JSProxyDispatcherPropertyHasNode(JSContext context, Object key, ReceiverCheckNode receiverCheck, boolean hasOwnProperty) {
super(receiverCheck);
this.hasOwnProperty = hasOwnProperty;
assert JSRuntime.isPropertyKey(key);
this.proxyGet = hasOwnProperty ? null : JSProxyHasPropertyNodeGen.create(context);
this.getOwnPropertyNode = hasOwnProperty ? JSGetOwnPropertyNode.create() : null;
}
@Override
protected boolean hasProperty(Object thisObj, HasPropertyCacheNode root) {
Object key = root.getKey();
JSDynamicObject store = receiverCheck.getStore(thisObj);
if (hasOwnProperty) {
return getOwnPropertyNode.execute(store, key) != null;
} else {
return proxyGet.executeWithTargetAndKeyBoolean(store, key);
}
}
}
public static final class ModuleNamespaceHasOwnPropertyNode extends LinkedHasPropertyCacheNode {
private final Location location;
@Child ReadImportBindingNode readBindingNode;
public ModuleNamespaceHasOwnPropertyNode(ReceiverCheckNode receiverCheck, Property property) {
super(receiverCheck);
assert JSProperty.isModuleNamespaceExport(property);
this.location = property.getLocation();
this.readBindingNode = ReadImportBindingNode.create();
}
@SuppressWarnings("deprecation")
@Override
protected boolean hasProperty(Object thisObj, HasPropertyCacheNode root) {
JSModuleNamespaceObject store = (JSModuleNamespaceObject) receiverCheck.getStore(thisObj);
ExportResolution.Resolved exportResolution = (ExportResolution.Resolved) location.get(store, receiverCheck.getShape());
// Throws ReferenceError in case of an uninitialized binding.
readBindingNode.execute(exportResolution);
return true;
}
}
public static final class UnspecializedHasPropertyCacheNode extends LinkedHasPropertyCacheNode {
public UnspecializedHasPropertyCacheNode(ReceiverCheckNode receiverCheckNode) {
super(receiverCheckNode);
}
@Override
protected boolean hasProperty(Object thisObj, HasPropertyCacheNode root) {
Object key = root.getKey();
if (root.isOwnProperty()) {
return JSObject.hasOwnProperty((JSDynamicObject) thisObj, key);
} else {
return JSObject.hasProperty((JSDynamicObject) thisObj, key);
}
}
}
public static final class GenericHasPropertyCacheNode extends HasCacheNode {
@Child private InteropLibrary interop;
private final JSClassProfile jsclassProfile = JSClassProfile.create();
public GenericHasPropertyCacheNode() {
super(null);
this.interop = InteropLibrary.getFactory().createDispatched(JSConfig.InteropLibraryLimit);
}
@InliningCutoff
@Override
protected boolean hasProperty(Object thisObj, HasPropertyCacheNode root) {
if (JSDynamicObject.isJSDynamicObject(thisObj)) {
Object key = root.getKey();
if (root.isOwnProperty()) {
return JSObject.hasOwnProperty((JSDynamicObject) thisObj, key, jsclassProfile);
} else {
return JSObject.hasProperty((JSDynamicObject) thisObj, key, jsclassProfile);
}
} else {
assert JSRuntime.isForeignObject(thisObj);
Object key = root.getKey();
if (key instanceof TruffleString propertyName) {
return interop.isMemberExisting(thisObj, Strings.toJavaString(propertyName));
} else {
return false;
}
}
}
}
public static final class ForeignHasPropertyCacheNode extends LinkedHasPropertyCacheNode {
@Child private InteropLibrary interop;
public ForeignHasPropertyCacheNode() {
super(new ForeignLanguageCheckNode());
this.interop = InteropLibrary.getFactory().createDispatched(JSConfig.InteropLibraryLimit);
}
@Override
protected boolean hasProperty(Object thisObj, HasPropertyCacheNode root) {
assert JSRuntime.isForeignObject(thisObj);
Object key = root.getKey();
if (key instanceof TruffleString propertyName) {
return interop.isMemberExisting(thisObj, Strings.toJavaString(propertyName));
} else {
return false;
}
}
}
/**
* Make a cache for a JSObject with this property map and requested property.
*
* @param property The particular entry of the property being accessed.
*/
@Override
protected HasCacheNode createCachedPropertyNode(Property property, Object thisObj, JSDynamicObject proto, int depth, Object value, HasCacheNode currentHead) {
assert !isOwnProperty() || depth == 0;
ReceiverCheckNode check;
if (JSDynamicObject.isJSDynamicObject(thisObj)) {
JSDynamicObject thisJSObj = (JSDynamicObject) thisObj;
Shape cacheShape = thisJSObj.getShape();
check = createShapeCheckNode(cacheShape, thisJSObj, proto, depth, false, false);
} else {
check = createPrimitiveReceiverCheck(thisObj, proto, depth);
}
if (hasOwnProperty && JSProperty.isModuleNamespaceExport(property)) {
return new ModuleNamespaceHasOwnPropertyNode(check, property);
}
return new PresentHasPropertyCacheNode(check);
}
@Override
protected HasCacheNode createUndefinedPropertyNode(Object thisObj, Object store, JSDynamicObject proto, int depth, Object value) {
HasCacheNode specialized = createJavaPropertyNodeMaybe(thisObj, proto, depth);
if (specialized != null) {
return specialized;
}
if (JSDynamicObject.isJSDynamicObject(thisObj)) {
JSDynamicObject thisJSObj = (JSDynamicObject) thisObj;
Shape cacheShape = thisJSObj.getShape();
if (JSAdapter.isJSAdapter(store)) {
return new JSAdapterHasPropertyCacheNode(key, createJSClassCheck(thisObj, proto, depth));
} else if (JSProxy.isJSProxy(store)) {
return new JSProxyDispatcherPropertyHasNode(context, key, createJSClassCheck(thisObj, proto, depth), isOwnProperty());
} else {
return new AbsentHasPropertyCacheNode(createShapeCheckNode(cacheShape, thisJSObj, proto, depth, false, false));
}
} else {
return new AbsentHasPropertyCacheNode(new InstanceofCheckNode(thisObj.getClass()));
}
}
@Override
protected HasCacheNode createJavaPropertyNodeMaybe(Object thisObj, JSDynamicObject proto, int depth) {
if (JavaPackage.isJavaPackage(thisObj)) {
return new PresentHasPropertyCacheNode(createJSClassCheck(thisObj, proto, depth));
} else if (JavaImporter.isJavaImporter(thisObj)) {
return new UnspecializedHasPropertyCacheNode(createJSClassCheck(thisObj, proto, depth));
}
return null;
}
/**
* Make a generic-case node, for when polymorphism becomes too high.
*/
@Override
protected HasCacheNode createGenericPropertyNode() {
return new GenericHasPropertyCacheNode();
}
@Override
protected boolean isPropertyAssumptionCheckEnabled() {
return propertyAssumptionCheckEnabled && context.isSingleRealm();
}
@Override
protected void setPropertyAssumptionCheckEnabled(boolean value) {
this.propertyAssumptionCheckEnabled = value;
}
@Override
protected boolean isGlobal() {
return false;
}
@Override
protected boolean isOwnProperty() {
return hasOwnProperty;
}
@Override
protected HasCacheNode createTruffleObjectPropertyNode() {
return new ForeignHasPropertyCacheNode();
}
@Override
protected boolean canCombineShapeCheck(Shape parentShape, Shape cacheShape, Object thisObj, int depth, Object value, Property property) {
assert shapesHaveCommonLayoutForKey(parentShape, cacheShape);
if (JSDynamicObject.isJSDynamicObject(thisObj) && JSProperty.isData(property)) {
assert depth == 0;
return !isGlobal();
}
return false;
}
@Override
protected HasCacheNode createCombinedIcPropertyNode(Shape parentShape, Shape cacheShape, Object thisObj, int depth, Object value, Property property) {
return new PresentHasPropertyCacheNode(new PropertyGetNode.CombinedShapeCheckNode(parentShape, cacheShape));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy