4949import static com .oracle .graal .python .nodes .BuiltinNames .TUPLE ;
5050import static com .oracle .graal .python .nodes .BuiltinNames .TYPE ;
5151import static com .oracle .graal .python .nodes .BuiltinNames .ZIP ;
52+ import static com .oracle .graal .python .nodes .SpecialAttributeNames .__DICT__ ;
5253import static com .oracle .graal .python .nodes .SpecialAttributeNames .__FILE__ ;
54+ import static com .oracle .graal .python .nodes .SpecialAttributeNames .__SLOTS__ ;
5355import static com .oracle .graal .python .nodes .SpecialMethodNames .DECODE ;
5456import static com .oracle .graal .python .nodes .SpecialMethodNames .__SETITEM__ ;
5557import static com .oracle .graal .python .runtime .exception .PythonErrorType .NotImplementedError ;
9193import com .oracle .graal .python .builtins .objects .function .PFunction ;
9294import com .oracle .graal .python .builtins .objects .function .PKeyword ;
9395import com .oracle .graal .python .builtins .objects .function .PythonCallable ;
96+ import com .oracle .graal .python .builtins .objects .getsetdescriptor .HiddenKeyDescriptor ;
9497import com .oracle .graal .python .builtins .objects .ints .PInt ;
9598import com .oracle .graal .python .builtins .objects .iterator .PZip ;
9699import com .oracle .graal .python .builtins .objects .list .PList ;
124127import com .oracle .graal .python .nodes .control .GetNextNode ;
125128import com .oracle .graal .python .nodes .datamodel .IsIndexNode ;
126129import com .oracle .graal .python .nodes .datamodel .IsSequenceNode ;
130+ import com .oracle .graal .python .nodes .expression .CastToListNode ;
127131import com .oracle .graal .python .nodes .function .PythonBuiltinBaseNode ;
128132import com .oracle .graal .python .nodes .function .PythonBuiltinNode ;
129133import com .oracle .graal .python .nodes .function .builtins .PythonBinaryBuiltinNode ;
140144import com .oracle .graal .python .runtime .exception .PythonErrorType ;
141145import com .oracle .graal .python .runtime .sequence .PSequence ;
142146import com .oracle .graal .python .runtime .sequence .storage .ByteSequenceStorage ;
147+ import com .oracle .graal .python .runtime .sequence .storage .SequenceStorage ;
143148import com .oracle .truffle .api .CompilerAsserts ;
144149import com .oracle .truffle .api .CompilerDirectives ;
145150import com .oracle .truffle .api .CompilerDirectives .CompilationFinal ;
151156import com .oracle .truffle .api .dsl .TypeSystemReference ;
152157import com .oracle .truffle .api .frame .VirtualFrame ;
153158import com .oracle .truffle .api .nodes .UnexpectedResultException ;
159+ import com .oracle .truffle .api .object .HiddenKey ;
154160import com .oracle .truffle .api .profiles .BranchProfile ;
155161import com .oracle .truffle .api .profiles .ConditionProfile ;
156162
@@ -1598,6 +1604,9 @@ public abstract static class TypeNode extends PythonBuiltinNode {
15981604 @ Child private ReadAttributeFromObjectNode readAttrNode ;
15991605 @ Child private WriteAttributeToObjectNode writeAttrNode ;
16001606 @ Child private CastToIndexNode castToInt ;
1607+ @ Child private CastToListNode castToList ;
1608+ @ Child private SequenceStorageNodes .LenNode slotLenNode ;
1609+ @ Child private SequenceStorageNodes .GetItemNode getSlotItemNode ;
16011610
16021611 @ Specialization (guards = {"isNoValue(bases)" , "isNoValue(dict)" })
16031612 @ SuppressWarnings ("unused" )
@@ -1612,12 +1621,15 @@ public Object type(VirtualFrame frame, PythonClass cls, String name, PTuple base
16121621 @ Cached ("create(__NEW__)" ) LookupInheritedAttributeNode getNewFuncNode ,
16131622 @ Cached ("create()" ) CallDispatchNode callNewFuncNode ,
16141623 @ Cached ("create()" ) CreateArgumentsNode createArgs ) {
1624+ // Determine the proper metatype to deal with this
16151625 PythonClass metaclass = calculate_metaclass (cls , bases , getMetaclassNode );
1626+
16161627 if (metaclass != cls ) {
16171628 Object newFunc = getNewFuncNode .execute (metaclass );
16181629 if (newFunc instanceof PBuiltinFunction && (((PBuiltinFunction ) newFunc ).getFunctionRootNode () == getRootNode ())) {
1619- // the new metaclass has the same __new__ function as we are in
1630+ // the new metaclass has the same __new__ function as we are in, continue
16201631 } else {
1632+ // Pass it to the winner
16211633 return callNewFuncNode .executeCall (frame , newFunc , createArgs .execute (metaclass , name , bases , namespace ), kwds );
16221634 }
16231635 }
@@ -1626,12 +1638,11 @@ public Object type(VirtualFrame frame, PythonClass cls, String name, PTuple base
16261638
16271639 @ TruffleBoundary
16281640 private Object typeMetaclass (String name , PTuple bases , PDict namespace , PythonClass metaclass ) {
1629- if (name .indexOf ('\0' ) != -1 ) {
1630- throw raise (ValueError , "type name must not contain null characters" );
1631- }
1641+
16321642 Object [] array = bases .getArray ();
16331643 PythonClass [] basesArray ;
16341644 if (array .length == 0 ) {
1645+ // Adjust for empty tuple bases
16351646 basesArray = new PythonClass []{getCore ().lookupType (PythonBuiltinClassType .PythonObject )};
16361647 } else {
16371648 basesArray = new PythonClass [array .length ];
@@ -1645,15 +1656,106 @@ private Object typeMetaclass(String name, PTuple bases, PDict namespace, PythonC
16451656 }
16461657 }
16471658 assert metaclass != null ;
1659+
1660+ if (name .indexOf ('\0' ) != -1 ) {
1661+ throw raise (ValueError , "type name must not contain null characters" );
1662+ }
16481663 PythonClass pythonClass = factory ().createPythonClass (metaclass , name , basesArray );
1664+
1665+ // copy the dictionary slots over, as CPython does through PyDict_Copy
1666+ // Also check for a __slots__ sequence variable in dict
1667+ Object slots = null ;
16491668 for (DictEntry entry : namespace .entries ()) {
1650- pythonClass .setAttribute (entry .getKey (), entry .getValue ());
1669+ Object key = entry .getKey ();
1670+ Object value = entry .getValue ();
1671+ if (__SLOTS__ .equals (key )) {
1672+ slots = value ;
1673+ } else {
1674+ pythonClass .setAttribute (key , value );
1675+ }
16511676 }
1652- addDictIfNative (pythonClass );
1677+
1678+ if (slots == null ) {
1679+ // takes care of checking if we may_add_dict and adds it if needed
1680+ addDictIfNative (pythonClass );
1681+ // TODO: tfel - also deal with weaklistoffset
1682+ } else {
1683+ // have slots
1684+
1685+ // Make it into a list
1686+ SequenceStorage slotList ;
1687+ if (slots instanceof String ) {
1688+ slotList = factory ().createList (new Object []{slots }).getSequenceStorage ();
1689+ } else {
1690+ slotList = getCastToListNode ().executeWith (slots ).getSequenceStorage ();
1691+ }
1692+ int slotlen = getListLenNode ().execute (slotList );
1693+ // TODO: tfel - check if slots are allowed. They are not if the base class is var
1694+ // sized
1695+
1696+ for (int i = 0 ; i < slotlen ; i ++) {
1697+ String slotName ;
1698+ Object element = getSlotItemNode ().execute (slotList , i );
1699+ // Check valid slot name
1700+ if (element instanceof String ) {
1701+ slotName = (String ) element ;
1702+ } else {
1703+ throw raise (TypeError , "__slots__ items must be strings, not '%p'" , element );
1704+ }
1705+ if (__DICT__ .equals (slotName )) {
1706+ // check that the native base does not already have tp_dictoffset
1707+ if (addDictIfNative (pythonClass )) {
1708+ throw raise (TypeError , "__dict__ slot disallowed: we already got one" );
1709+ }
1710+ } else {
1711+ // TODO: check for __weakref__
1712+ // TODO: Copy slots into a list, mangle names and sort them
1713+ HiddenKey hiddenSlotKey = new HiddenKey (slotName );
1714+ HiddenKeyDescriptor slotDesc = factory ().createHiddenKeyDescriptor (hiddenSlotKey , pythonClass );
1715+ pythonClass .setAttribute (slotName , slotDesc );
1716+ }
1717+ // Make slots into a tuple
1718+ }
1719+ pythonClass .setAttribute (__SLOTS__ , factory ().createTuple (slotList ));
1720+ if (basesArray .length > 1 ) {
1721+ // TODO: tfel - check if secondary bases provide weakref or dict when we don't
1722+ // already have one
1723+ }
1724+ }
1725+
1726+ // TODO: tfel special case __new__: if it's a plain function, make it a static function
1727+ // TODO: tfel Special-case __init_subclass__: if it's a plain function, make it a
1728+ // classmethod
1729+
16531730 return pythonClass ;
16541731 }
16551732
1656- private void addDictIfNative (PythonClass pythonClass ) {
1733+ private SequenceStorageNodes .GetItemNode getSlotItemNode () {
1734+ if (getSlotItemNode == null ) {
1735+ CompilerDirectives .transferToInterpreterAndInvalidate ();
1736+ getSlotItemNode = insert (SequenceStorageNodes .GetItemNode .create ());
1737+ }
1738+ return getSlotItemNode ;
1739+ }
1740+
1741+ private SequenceStorageNodes .LenNode getListLenNode () {
1742+ if (slotLenNode == null ) {
1743+ CompilerDirectives .transferToInterpreterAndInvalidate ();
1744+ slotLenNode = insert (SequenceStorageNodes .LenNode .create ());
1745+ }
1746+ return slotLenNode ;
1747+ }
1748+
1749+ private CastToListNode getCastToListNode () {
1750+ if (castToList == null ) {
1751+ CompilerDirectives .transferToInterpreterAndInvalidate ();
1752+ castToList = insert (CastToListNode .create ());
1753+ }
1754+ return castToList ;
1755+ }
1756+
1757+ private boolean addDictIfNative (PythonClass pythonClass ) {
1758+ boolean addedNewDict = false ;
16571759 for (Object cls : pythonClass .getMethodResolutionOrder ()) {
16581760 if (cls instanceof PythonNativeClass ) {
16591761 if (readAttrNode == null ) {
@@ -1666,6 +1768,7 @@ private void addDictIfNative(PythonClass pythonClass) {
16661768 long basicsize = castToInt .execute (readAttrNode .execute (cls , SpecialAttributeNames .__BASICSIZE__ ));
16671769 long itemsize = castToInt .execute (readAttrNode .execute (cls , SpecialAttributeNames .__ITEMSIZE__ ));
16681770 if (dictoffset == 0 ) {
1771+ addedNewDict = true ;
16691772 // add_dict
16701773 if (itemsize != 0 ) {
16711774 dictoffset = -SIZEOF_PY_OBJECT_PTR ;
@@ -1680,6 +1783,7 @@ private void addDictIfNative(PythonClass pythonClass) {
16801783 break ;
16811784 }
16821785 }
1786+ return addedNewDict ;
16831787 }
16841788
16851789 private PythonClass calculate_metaclass (PythonClass cls , PTuple bases , GetClassNode getMetaclassNode ) {
0 commit comments