Skip to content

Commit 3b126b4

Browse files
committed
Implement check for abstract methods
1 parent 457c9b0 commit 3b126b4

File tree

6 files changed

+123
-7
lines changed

6 files changed

+123
-7
lines changed

graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_abc.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_ABC_has___slots__
22
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_ABC_has___slots__
3+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_ABC_helper
4+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_ABC_helper
5+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_abstractclassmethod_basics
6+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_abstractclassmethod_basics
37
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_abstractmethod_basics
48
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_abstractmethod_basics
9+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_abstractmethod_integration
10+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_abstractmethod_integration
11+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_abstractproperty_basics
12+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_abstractproperty_basics
13+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_abstractstaticmethod_basics
14+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_abstractstaticmethod_basics
515
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_all_new_methods_are_called
616
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_all_new_methods_are_called
17+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_customdescriptors_with_abstractmethod
18+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_customdescriptors_with_abstractmethod
19+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_descriptors_with_abstractmethod
20+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_descriptors_with_abstractmethod
721
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_isinstance_invalidation
822
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_isinstance_invalidation
923
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_issubclass_bad_arguments
@@ -26,3 +40,9 @@
2640
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABC.test_tricky_new_works
2741
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABCWithInitSubclass.test_works_with_init_subclass
2842
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestABCWithInitSubclass.test_works_with_init_subclass
43+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestLegacyAPI.test_abstractclassmethod_basics
44+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestLegacyAPI.test_abstractclassmethod_basics
45+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestLegacyAPI.test_abstractproperty_basics
46+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestLegacyAPI.test_abstractproperty_basics
47+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestLegacyAPI.test_abstractstaticmethod_basics
48+
*graalpython.lib-python.3.test.test_abc.test_factory.<locals>.TestLegacyAPI.test_abstractstaticmethod_basics

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinConstructors.java

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import static com.oracle.graal.python.nodes.ErrorMessages.TAKES_EXACTLY_D_ARGUMENTS_D_GIVEN;
6767
import static com.oracle.graal.python.nodes.PGuards.isInteger;
6868
import static com.oracle.graal.python.nodes.PGuards.isNoValue;
69+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__ABSTRACTMETHODS__;
6970
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__BASICSIZE__;
7071
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__CLASSCELL__;
7172
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__DICTOFFSET__;
@@ -180,6 +181,7 @@
180181
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
181182
import com.oracle.graal.python.builtins.objects.type.PythonClass;
182183
import com.oracle.graal.python.builtins.objects.type.PythonManagedClass;
184+
import com.oracle.graal.python.builtins.objects.type.TypeFlags;
183185
import com.oracle.graal.python.builtins.objects.type.TypeNodes;
184186
import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetBestBaseClassNode;
185187
import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetItemsizeNode;
@@ -190,6 +192,8 @@
190192
import com.oracle.graal.python.nodes.BuiltinNames;
191193
import com.oracle.graal.python.nodes.ErrorMessages;
192194
import com.oracle.graal.python.nodes.PGuards;
195+
import com.oracle.graal.python.nodes.PNodeWithContext;
196+
import com.oracle.graal.python.nodes.PRaiseNode;
193197
import com.oracle.graal.python.nodes.SpecialAttributeNames;
194198
import com.oracle.graal.python.nodes.SpecialMethodNames;
195199
import com.oracle.graal.python.nodes.attributes.GetAttributeNode;
@@ -199,6 +203,7 @@
199203
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode;
200204
import com.oracle.graal.python.nodes.attributes.SetAttributeNode;
201205
import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode;
206+
import com.oracle.graal.python.nodes.builtins.ListNodes;
202207
import com.oracle.graal.python.nodes.builtins.TupleNodes;
203208
import com.oracle.graal.python.nodes.call.CallNode;
204209
import com.oracle.graal.python.nodes.call.special.LookupAndCallTernaryNode;
@@ -1629,11 +1634,34 @@ public abstract static class ObjectNode extends PythonVarargsBuiltinNode {
16291634
@Child private SplitArgsNode splitArgsNode;
16301635
@Child private LookupAttributeInMRONode lookupInit;
16311636
@Child private LookupAttributeInMRONode lookupNew;
1637+
@Child private ReportAbstractClassNode reportAbstractClassNode;
16321638
@CompilationFinal private ValueProfile profileInit;
16331639
@CompilationFinal private ValueProfile profileNew;
16341640
@CompilationFinal private ValueProfile profileInitFactory;
16351641
@CompilationFinal private ValueProfile profileNewFactory;
16361642

1643+
abstract static class ReportAbstractClassNode extends PNodeWithContext {
1644+
public abstract PException execute(VirtualFrame frame, Object type);
1645+
1646+
@Specialization
1647+
static PException report(VirtualFrame frame, Object type,
1648+
@CachedLibrary(limit = "2") PythonObjectLibrary lib,
1649+
@Cached ReadAttributeFromObjectNode readAttributeFromObjectNode,
1650+
@Cached CastToJavaStringNode cast,
1651+
@Cached ListNodes.ConstructListNode constructListNode,
1652+
@Cached PRaiseNode raiseNode) {
1653+
PList list = constructListNode.execute(readAttributeFromObjectNode.execute(type, __ABSTRACTMETHODS__));
1654+
int methodCount = lib.lengthWithFrame(list, frame);
1655+
lib.lookupAndCallRegularMethod(list, frame, "sort");
1656+
String joined = cast.execute(lib.lookupAndCallRegularMethod(", ", frame, "join", list));
1657+
throw raiseNode.raise(TypeError, "Can't instantiate abstract class %N with abstract method%s %s", type, methodCount > 1 ? "s" : "", joined);
1658+
}
1659+
1660+
public static ReportAbstractClassNode create() {
1661+
return BuiltinConstructorsFactory.ObjectNodeFactory.ReportAbstractClassNodeGen.create();
1662+
}
1663+
}
1664+
16371665
@Override
16381666
public final Object varArgExecute(VirtualFrame frame, @SuppressWarnings("unused") Object self, Object[] arguments, PKeyword[] keywords) throws VarargsBuiltinDirectInvocationNotSupported {
16391667
if (splitArgsNode == null) {
@@ -1644,8 +1672,11 @@ public final Object varArgExecute(VirtualFrame frame, @SuppressWarnings("unused"
16441672
}
16451673

16461674
@Specialization(guards = {"!self.needsNativeAllocation()"})
1647-
Object doManagedObject(PythonManagedClass self, Object[] varargs, PKeyword[] kwargs) {
1675+
Object doManagedObject(VirtualFrame frame, PythonManagedClass self, Object[] varargs, PKeyword[] kwargs) {
16481676
checkExcessArgs(self, varargs, kwargs);
1677+
if (self.isAbstractClass()) {
1678+
throw getReportAbstractClassNode().execute(frame, self);
1679+
}
16491680
return factory().createPythonObject(self);
16501681
}
16511682

@@ -1656,16 +1687,23 @@ Object doBuiltinTypeType(PythonBuiltinClassType self, Object[] varargs, PKeyword
16561687
}
16571688

16581689
@Specialization(guards = "self.needsNativeAllocation()")
1659-
Object doNativeObjectIndirect(PythonManagedClass self, Object[] varargs, PKeyword[] kwargs,
1690+
Object doNativeObjectIndirect(VirtualFrame frame, PythonManagedClass self, Object[] varargs, PKeyword[] kwargs,
16601691
@Cached("create()") GetMroNode getMroNode) {
16611692
checkExcessArgs(self, varargs, kwargs);
1693+
if (self.isAbstractClass()) {
1694+
throw getReportAbstractClassNode().execute(frame, self);
1695+
}
16621696
Object nativeBaseClass = findFirstNativeBaseClass(getMroNode.execute(self));
16631697
return callNativeGenericNewNode(nativeBaseClass, varargs, kwargs);
16641698
}
16651699

16661700
@Specialization(guards = "isNativeClass(self)")
1667-
Object doNativeObjectIndirect(Object self, Object[] varargs, PKeyword[] kwargs) {
1701+
Object doNativeObjectDirect(VirtualFrame frame, Object self, Object[] varargs, PKeyword[] kwargs,
1702+
@Cached TypeNodes.GetTypeFlagsNode getTypeFlagsNode) {
16681703
checkExcessArgs(self, varargs, kwargs);
1704+
if ((getTypeFlagsNode.execute(self) & TypeFlags.IS_ABSTRACT) != 0) {
1705+
throw getReportAbstractClassNode().execute(frame, self);
1706+
}
16691707
return callNativeGenericNewNode(self, varargs, kwargs);
16701708
}
16711709

@@ -1750,6 +1788,14 @@ private void checkExcessArgs(Object type, Object[] varargs, PKeyword[] kwargs) {
17501788
}
17511789
}
17521790
}
1791+
1792+
private ReportAbstractClassNode getReportAbstractClassNode() {
1793+
if (reportAbstractClassNode == null) {
1794+
CompilerDirectives.transferToInterpreterAndInvalidate();
1795+
reportAbstractClassNode = insert(ReportAbstractClassNode.create());
1796+
}
1797+
return reportAbstractClassNode;
1798+
}
17531799
}
17541800

17551801
// range(stop)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonManagedClass.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ public abstract class PythonManagedClass extends PythonObject implements PythonA
6565

6666
@CompilationFinal private MroSequenceStorage methodResolutionOrder;
6767

68+
private boolean abstractClass;
69+
6870
private final Set<PythonAbstractClass> subClasses = Collections.newSetFromMap(new WeakHashMap<PythonAbstractClass, Boolean>());
6971
@CompilationFinal private Shape instanceShape;
7072
private String name;
@@ -176,6 +178,14 @@ public void setName(String name) {
176178
this.name = name;
177179
}
178180

181+
public boolean isAbstractClass() {
182+
return abstractClass;
183+
}
184+
185+
public void setAbstractClass(boolean abstractClass) {
186+
this.abstractClass = abstractClass;
187+
}
188+
179189
private boolean computeNeedsNativeAllocation() {
180190
for (PythonAbstractClass cls : getMethodResolutionOrder().getInternalClassArray()) {
181191
if (PGuards.isNativeClass(cls)) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@
2727
package com.oracle.graal.python.builtins.objects.type;
2828

2929
import static com.oracle.graal.python.builtins.objects.str.StringUtils.containsNullCharacter;
30+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__ABSTRACTMETHODS__;
3031
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__BASES__;
3132
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__BASE__;
3233
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__BASICSIZE__;
3334
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__CLASS__;
3435
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__DICTOFFSET__;
3536
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__DICT__;
37+
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__DOC__;
3638
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__FLAGS__;
3739
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__ITEMSIZE__;
3840
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__MODULE__;
@@ -65,12 +67,12 @@
6567
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
6668
import com.oracle.graal.python.builtins.PythonBuiltins;
6769
import com.oracle.graal.python.builtins.objects.PNone;
68-
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
69-
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.GetTypeMemberNode;
70-
import com.oracle.graal.python.builtins.objects.cext.capi.NativeMember;
7170
import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject;
7271
import com.oracle.graal.python.builtins.objects.cext.PythonNativeClass;
7372
import com.oracle.graal.python.builtins.objects.cext.PythonNativeObject;
73+
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes;
74+
import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.GetTypeMemberNode;
75+
import com.oracle.graal.python.builtins.objects.cext.capi.NativeMember;
7476
import com.oracle.graal.python.builtins.objects.common.SequenceNodes.GetObjectArrayNode;
7577
import com.oracle.graal.python.builtins.objects.dict.PDict;
7678
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
@@ -98,7 +100,6 @@
98100
import com.oracle.graal.python.nodes.ErrorMessages;
99101
import com.oracle.graal.python.nodes.PGuards;
100102
import com.oracle.graal.python.nodes.SpecialAttributeNames;
101-
import static com.oracle.graal.python.nodes.SpecialAttributeNames.__DOC__;
102103
import com.oracle.graal.python.nodes.argument.positional.PositionalArgumentsNode;
103104
import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetFixedAttributeNode;
104105
import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode;
@@ -1253,4 +1254,37 @@ Object doGeneric(Object self,
12531254
throw raise(PythonErrorType.TypeError, "descriptor '__flags__' for 'type' objects doesn't apply to '%p' object", self);
12541255
}
12551256
}
1257+
1258+
@Builtin(name = __ABSTRACTMETHODS__, minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true)
1259+
@GenerateNodeFactory
1260+
abstract static class AbstracMethodsNode extends PythonBinaryBuiltinNode {
1261+
@Specialization(guards = "isNoValue(none)")
1262+
Object get(Object self, @SuppressWarnings("unused") PNone none,
1263+
@Cached IsSameTypeNode isSameTypeNode,
1264+
@Cached ReadAttributeFromObjectNode readAttributeFromObjectNode) {
1265+
// Avoid returning this descriptor
1266+
if (!isSameTypeNode.execute(self, PythonBuiltinClassType.PythonClass)) {
1267+
Object result = readAttributeFromObjectNode.execute(self, __ABSTRACTMETHODS__);
1268+
if (result != PNone.NO_VALUE) {
1269+
return result;
1270+
}
1271+
}
1272+
throw raise(AttributeError, ErrorMessages.OBJ_S_HAS_NO_ATTR_S, GetNameNode.getUncached().execute(self), __ABSTRACTMETHODS__);
1273+
}
1274+
1275+
@Specialization(guards = "!isNoValue(value)", limit = "3")
1276+
static Object set(VirtualFrame frame, PythonClass self, Object value,
1277+
@CachedLibrary("value") PythonObjectLibrary lib,
1278+
@Cached WriteAttributeToObjectNode writeAttributeToObjectNode) {
1279+
writeAttributeToObjectNode.execute(self, __ABSTRACTMETHODS__, value);
1280+
self.setAbstractClass(lib.isTrue(value, frame));
1281+
return PNone.NONE;
1282+
}
1283+
1284+
@Fallback
1285+
@SuppressWarnings("unused")
1286+
Object set(Object self, Object value) {
1287+
throw raise(AttributeError, ErrorMessages.CANT_SET_ATTRIBUTES_OF_TYPE_S, GetNameNode.getUncached().execute(self));
1288+
}
1289+
}
12561290
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import static com.oracle.graal.python.builtins.objects.type.TypeFlags.HAVE_GC;
5353
import static com.oracle.graal.python.builtins.objects.type.TypeFlags.HAVE_VECTORCALL;
5454
import static com.oracle.graal.python.builtins.objects.type.TypeFlags.HEAPTYPE;
55+
import static com.oracle.graal.python.builtins.objects.type.TypeFlags.IS_ABSTRACT;
5556
import static com.oracle.graal.python.builtins.objects.type.TypeFlags.LIST_SUBCLASS;
5657
import static com.oracle.graal.python.builtins.objects.type.TypeFlags.LONG_SUBCLASS;
5758
import static com.oracle.graal.python.builtins.objects.type.TypeFlags.METHOD_DESCRIPTOR;
@@ -268,6 +269,10 @@ private static long computeFlags(PythonClass clazz) {
268269
// should be fine to just set it.
269270
long result = DEFAULT | HEAPTYPE | BASETYPE | HAVE_GC;
270271

272+
if (clazz.isAbstractClass()) {
273+
result |= IS_ABSTRACT;
274+
}
275+
271276
// flags are inherited
272277
MroSequenceStorage mroStorage = GetMroStorageNodeGen.getUncached().execute(clazz);
273278
int n = SequenceStorageNodes.LenNode.getUncached().execute(mroStorage);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/SpecialAttributeNames.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,5 @@ public abstract class SpecialAttributeNames {
7979
public static final String __WEAKLISTOFFSET__ = "__weaklistoffset__";
8080
public static final String __ALL__ = "__all__";
8181
public static final String __FLAGS__ = "__flags__";
82+
public static final String __ABSTRACTMETHODS__ = "__abstractmethods__";
8283
}

0 commit comments

Comments
 (0)