Skip to content

Commit 00eb271

Browse files
committed
Improve attribute handling of foreign objects.
1 parent 406a7c3 commit 00eb271

File tree

1 file changed

+161
-3
lines changed

1 file changed

+161
-3
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/foreign/TruffleObjectBuiltins.java

Lines changed: 161 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,19 @@
2626

2727
package com.oracle.graal.python.builtins.objects.foreign;
2828

29+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__CLASS__;
2930
import static com.oracle.graal.python.nodes.SpecialMethodNames.__ADD__;
3031
import static com.oracle.graal.python.nodes.SpecialMethodNames.__BOOL__;
3132
import static com.oracle.graal.python.nodes.SpecialMethodNames.__CALL__;
3233
import static com.oracle.graal.python.nodes.SpecialMethodNames.__DELATTR__;
34+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__DELETE__;
3335
import static com.oracle.graal.python.nodes.SpecialMethodNames.__DELITEM__;
3436
import static com.oracle.graal.python.nodes.SpecialMethodNames.__DIR__;
3537
import static com.oracle.graal.python.nodes.SpecialMethodNames.__FLOORDIV__;
3638
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GETATTRIBUTE__;
39+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GETATTR__;
3740
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GETITEM__;
41+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GET__;
3842
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GE__;
3943
import static com.oracle.graal.python.nodes.SpecialMethodNames.__GT__;
4044
import static com.oracle.graal.python.nodes.SpecialMethodNames.__ITER__;
@@ -50,6 +54,7 @@
5054
import static com.oracle.graal.python.nodes.SpecialMethodNames.__RTRUEDIV__;
5155
import static com.oracle.graal.python.nodes.SpecialMethodNames.__SETATTR__;
5256
import static com.oracle.graal.python.nodes.SpecialMethodNames.__SETITEM__;
57+
import static com.oracle.graal.python.nodes.SpecialMethodNames.__SET__;
5358
import static com.oracle.graal.python.nodes.SpecialMethodNames.__SUB__;
5459
import static com.oracle.graal.python.nodes.SpecialMethodNames.__TRUEDIV__;
5560
import static com.oracle.graal.python.runtime.exception.PythonErrorType.AttributeError;
@@ -64,8 +69,14 @@
6469
import com.oracle.graal.python.builtins.objects.PNone;
6570
import com.oracle.graal.python.builtins.objects.PNotImplemented;
6671
import com.oracle.graal.python.builtins.objects.function.PKeyword;
72+
import com.oracle.graal.python.builtins.objects.function.PythonCallable;
6773
import com.oracle.graal.python.builtins.objects.list.PList;
74+
import com.oracle.graal.python.builtins.objects.type.PythonClass;
6875
import com.oracle.graal.python.nodes.PGuards;
76+
import com.oracle.graal.python.nodes.SpecialMethodNames;
77+
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
78+
import com.oracle.graal.python.nodes.attributes.LookupInheritedAttributeNode;
79+
import com.oracle.graal.python.nodes.call.special.CallTernaryMethodNode;
6980
import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
7081
import com.oracle.graal.python.nodes.expression.BinaryArithmetic;
7182
import com.oracle.graal.python.nodes.expression.BinaryComparisonNode;
@@ -76,6 +87,7 @@
7687
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
7788
import com.oracle.graal.python.nodes.interop.PForeignToPTypeNode;
7889
import com.oracle.graal.python.nodes.interop.PTypeToForeignNode;
90+
import com.oracle.graal.python.nodes.object.GetClassNode;
7991
import com.oracle.graal.python.runtime.exception.PythonErrorType;
8092
import com.oracle.truffle.api.CompilerDirectives;
8193
import com.oracle.truffle.api.dsl.Cached;
@@ -92,6 +104,8 @@
92104
import com.oracle.truffle.api.interop.UnsupportedMessageException;
93105
import com.oracle.truffle.api.interop.UnsupportedTypeException;
94106
import com.oracle.truffle.api.nodes.Node;
107+
import com.oracle.truffle.api.profiles.BranchProfile;
108+
import com.oracle.truffle.api.profiles.ConditionProfile;
95109
import com.oracle.truffle.api.profiles.ValueProfile;
96110

97111
@CoreFunctions(extendClasses = TruffleObject.class)
@@ -790,16 +804,149 @@ protected Object doGeneric(Object callee, @SuppressWarnings("unused") Object arg
790804

791805
@Builtin(name = __GETATTRIBUTE__, fixedNumOfArguments = 2)
792806
@GenerateNodeFactory
793-
abstract static class GetattributeNode extends UnboxNode {
794-
@Specialization(guards = "isForeignObject(object)")
807+
public abstract static class GetattributeNode extends PythonBinaryBuiltinNode {
808+
private final BranchProfile hasDescProfile = BranchProfile.create();
809+
private final BranchProfile isDescProfile = BranchProfile.create();
810+
private final BranchProfile hasValueProfile = BranchProfile.create();
811+
private final BranchProfile errorProfile = BranchProfile.create();
812+
private final ConditionProfile typeIsObjectProfile = ConditionProfile.createBinaryProfile();
813+
814+
@Child private LookupInheritedAttributeNode lookup = LookupInheritedAttributeNode.create();
815+
private final ValueProfile typeProfile = ValueProfile.createIdentityProfile();
816+
@Child private GetClassNode getObjectClassNode;
817+
@Child private GetClassNode getDataClassNode;
818+
@Child private LookupAttributeInMRONode lookupGetNode;
819+
@Child private LookupAttributeInMRONode lookupSetNode;
820+
@Child private LookupAttributeInMRONode lookupDeleteNode;
821+
@Child private CallTernaryMethodNode dispatchGet;
822+
@Child private Node attrReadNode;
823+
@Child private LookupAndCallBinaryNode getattrNode;
824+
825+
@Specialization
826+
protected Object doIt(TruffleObject object, Object key) {
827+
Object descr = lookup.execute(object, key);
828+
PythonClass dataDescClass = null;
829+
if (descr != PNone.NO_VALUE) {
830+
hasDescProfile.enter();
831+
dataDescClass = getDataClass(descr);
832+
Object delete = PNone.NO_VALUE;
833+
Object set = lookupSet(dataDescClass);
834+
if (set == PNone.NO_VALUE) {
835+
delete = lookupDelete(dataDescClass);
836+
}
837+
if (set != PNone.NO_VALUE || delete != PNone.NO_VALUE) {
838+
isDescProfile.enter();
839+
Object get = lookupGet(dataDescClass);
840+
if (get instanceof PythonCallable) {
841+
// Only override if __get__ is defined, too, for compatibility with CPython.
842+
return dispatch(object, descr, get);
843+
}
844+
}
845+
}
846+
Object value = readAttribute(object, key);
847+
if (value != PNone.NO_VALUE) {
848+
hasValueProfile.enter();
849+
return value;
850+
}
851+
if (descr != PNone.NO_VALUE) {
852+
hasDescProfile.enter();
853+
Object get = lookupGet(dataDescClass);
854+
if (get == PNone.NO_VALUE) {
855+
return descr;
856+
} else if (get instanceof PythonCallable) {
857+
return dispatch(object, descr, get);
858+
}
859+
}
860+
errorProfile.enter();
861+
return fallbackGetattr(object, key);
862+
}
863+
864+
private Object fallbackGetattr(Object object, Object key) {
865+
if (getattrNode == null) {
866+
CompilerDirectives.transferToInterpreterAndInvalidate();
867+
getattrNode = insert(LookupAndCallBinaryNode.create(SpecialMethodNames.__GETATTR__));
868+
}
869+
return getattrNode.executeObject(object, key);
870+
}
871+
872+
private Object readAttribute(TruffleObject object, Object key) {
873+
if (attrReadNode == null) {
874+
CompilerDirectives.transferToInterpreterAndInvalidate();
875+
attrReadNode = insert(Message.READ.createNode());
876+
}
877+
try {
878+
return ForeignAccess.sendRead(attrReadNode, object, key);
879+
} catch (UnknownIdentifierException | UnsupportedMessageException e) {
880+
return PNone.NO_VALUE;
881+
}
882+
}
883+
884+
private Object dispatch(Object object, Object descr, Object get) {
885+
if (dispatchGet == null) {
886+
CompilerDirectives.transferToInterpreterAndInvalidate();
887+
dispatchGet = insert(CallTernaryMethodNode.create());
888+
}
889+
PythonClass type = getObjectClass(object);
890+
return dispatchGet.execute(get, descr, typeIsObjectProfile.profile(type == object) ? PNone.NONE : object, type);
891+
}
892+
893+
private Object lookupGet(PythonClass dataDescClass) {
894+
if (lookupGetNode == null) {
895+
CompilerDirectives.transferToInterpreterAndInvalidate();
896+
lookupGetNode = insert(LookupAttributeInMRONode.create());
897+
}
898+
return lookupGetNode.execute(dataDescClass, __GET__);
899+
}
900+
901+
private Object lookupDelete(PythonClass dataDescClass) {
902+
if (lookupDeleteNode == null) {
903+
CompilerDirectives.transferToInterpreterAndInvalidate();
904+
lookupDeleteNode = insert(LookupAttributeInMRONode.create());
905+
}
906+
return lookupDeleteNode.execute(dataDescClass, __DELETE__);
907+
}
908+
909+
private Object lookupSet(PythonClass dataDescClass) {
910+
if (lookupSetNode == null) {
911+
CompilerDirectives.transferToInterpreterAndInvalidate();
912+
lookupSetNode = insert(LookupAttributeInMRONode.create());
913+
}
914+
return lookupSetNode.execute(dataDescClass, __SET__);
915+
}
916+
917+
private PythonClass getObjectClass(Object object) {
918+
if (getObjectClassNode == null) {
919+
CompilerDirectives.transferToInterpreterAndInvalidate();
920+
getObjectClassNode = insert(GetClassNode.create());
921+
}
922+
return typeProfile.profile(getObjectClassNode.execute(object));
923+
}
924+
925+
private PythonClass getDataClass(Object descr) {
926+
if (getDataClassNode == null) {
927+
CompilerDirectives.transferToInterpreterAndInvalidate();
928+
getDataClassNode = insert(GetClassNode.create());
929+
}
930+
return getDataClassNode.execute(descr);
931+
}
932+
}
933+
934+
@Builtin(name = __GETATTR__, fixedNumOfArguments = 3)
935+
@GenerateNodeFactory
936+
public abstract static class GetattrNode extends PythonBinaryBuiltinNode {
937+
@Specialization(guards = {"isForeignObject(object)"})
795938
protected Object doIt(TruffleObject object, Object key,
796-
@Cached("READ.createNode()") Node readNode) {
939+
@Cached("createReadNode()") Node readNode) {
797940
try {
798941
return ForeignAccess.sendRead(readNode, object, key);
799942
} catch (UnknownIdentifierException | UnsupportedMessageException e) {
800943
throw raise(PythonErrorType.AttributeError, "foreign object %s has no attribute %s", object, key);
801944
}
802945
}
946+
947+
protected Node createReadNode() {
948+
return Message.READ.createNode();
949+
}
803950
}
804951

805952
@Builtin(name = __GETITEM__, fixedNumOfArguments = 2)
@@ -883,4 +1030,15 @@ protected Object doIt(TruffleObject object,
8831030
}
8841031
}
8851032
}
1033+
1034+
@Builtin(name = __CLASS__, fixedNumOfArguments = 1, isGetter = true)
1035+
@GenerateNodeFactory
1036+
public abstract static class ClassNode extends PythonUnaryBuiltinNode {
1037+
@Specialization(guards = {"isForeignObject(object)"})
1038+
protected Object doIt(TruffleObject object,
1039+
@Cached("create()") GetClassNode getClassNode) {
1040+
return getClassNode.execute(object);
1041+
}
1042+
}
1043+
8861044
}

0 commit comments

Comments
 (0)