Commit 97cb7314 authored by Stefan Behnel's avatar Stefan Behnel

ticket #607: access inner fields of CPython's builtin objects

--HG--
rename : tests/broken/builtinslice.pyx => tests/run/builtinslice.pyx
parent a51f81b7
...@@ -300,6 +300,22 @@ class _BuiltinOverride(object): ...@@ -300,6 +300,22 @@ class _BuiltinOverride(object):
self.func_type, self.sig = func_type, sig self.func_type, self.sig = func_type, sig
self.utility_code = utility_code self.utility_code = utility_code
class BuiltinAttribute(object):
def __init__(self, py_name, cname=None, field_type=None, field_type_name=None):
self.py_name = py_name
self.cname = cname or py_name
self.field_type_name = field_type_name # can't do the lookup before the type is declared!
self.field_type = field_type
def declare_in_type(self, self_type):
if self.field_type_name is not None:
# lazy type lookup
field_type = builtin_scope.lookup(self.field_type_name).type
else:
field_type = self.field_type or PyrexTypes.py_object_type
entry = self_type.scope.declare(self.py_name, self.cname, field_type, None, 'private')
entry.is_variable = True
class BuiltinFunction(_BuiltinOverride): class BuiltinFunction(_BuiltinOverride):
def declare_in_scope(self, scope): def declare_in_scope(self, scope):
func_type, sig = self.func_type, self.sig func_type, sig = self.func_type, self.sig
...@@ -426,9 +442,10 @@ builtin_types_table = [ ...@@ -426,9 +442,10 @@ builtin_types_table = [
("long", "PyLong_Type", []), ("long", "PyLong_Type", []),
("float", "PyFloat_Type", []), ("float", "PyFloat_Type", []),
# Until we have a way to access attributes of a type, ("complex", "PyComplex_Type", [BuiltinAttribute('cval', field_type_name = 'Py_complex'),
# we don't want to make this one builtin. BuiltinAttribute('real', 'cval.real', field_type = PyrexTypes.c_double_type),
# ("complex", "PyComplex_Type", []), BuiltinAttribute('imag', 'cval.imag', field_type = PyrexTypes.c_double_type),
]),
("bytes", "PyBytes_Type", []), ("bytes", "PyBytes_Type", []),
("str", "PyString_Type", []), ("str", "PyString_Type", []),
...@@ -447,7 +464,10 @@ builtin_types_table = [ ...@@ -447,7 +464,10 @@ builtin_types_table = [
BuiltinMethod("values","T", "O", "PyDict_Values"), # FIXME: Py3 mode? BuiltinMethod("values","T", "O", "PyDict_Values"), # FIXME: Py3 mode?
BuiltinMethod("copy", "T", "T", "PyDict_Copy")]), BuiltinMethod("copy", "T", "T", "PyDict_Copy")]),
("slice", "PySlice_Type", []), ("slice", "PySlice_Type", [BuiltinAttribute('start'),
BuiltinAttribute('stop'),
BuiltinAttribute('step'),
]),
# ("file", "PyFile_Type", []), # not in Py3 # ("file", "PyFile_Type", []), # not in Py3
("set", "PySet_Type", [BuiltinMethod("clear", "T", "i", "PySet_Clear"), ("set", "PySet_Type", [BuiltinMethod("clear", "T", "i", "PySet_Clear"),
...@@ -480,6 +500,10 @@ builtin_structs_table = [ ...@@ -480,6 +500,10 @@ builtin_structs_table = [
("strides", PyrexTypes.c_py_ssize_t_ptr_type), ("strides", PyrexTypes.c_py_ssize_t_ptr_type),
("suboffsets", PyrexTypes.c_py_ssize_t_ptr_type), ("suboffsets", PyrexTypes.c_py_ssize_t_ptr_type),
("internal", PyrexTypes.c_void_ptr_type), ("internal", PyrexTypes.c_void_ptr_type),
]),
('Py_complex', 'Py_complex',
[('real', PyrexTypes.c_double_type),
('imag', PyrexTypes.c_double_type),
]) ])
] ]
...@@ -497,7 +521,13 @@ def init_builtin_types(): ...@@ -497,7 +521,13 @@ def init_builtin_types():
global builtin_types global builtin_types
for name, cname, methods in builtin_types_table: for name, cname, methods in builtin_types_table:
utility = builtin_utility_code.get(name) utility = builtin_utility_code.get(name)
the_type = builtin_scope.declare_builtin_type(name, cname, utility) if name == 'frozenset':
objstruct_cname = 'PySetObject'
elif name == 'bool':
objstruct_cname = None
else:
objstruct_cname = 'Py%sObject' % name.capitalize()
the_type = builtin_scope.declare_builtin_type(name, cname, utility, objstruct_cname)
builtin_types[name] = the_type builtin_types[name] = the_type
for method in methods: for method in methods:
method.declare_in_type(the_type) method.declare_in_type(the_type)
...@@ -512,9 +542,9 @@ def init_builtin_structs(): ...@@ -512,9 +542,9 @@ def init_builtin_structs():
name, "struct", scope, 1, None, cname = cname) name, "struct", scope, 1, None, cname = cname)
def init_builtins(): def init_builtins():
init_builtin_structs()
init_builtin_funcs() init_builtin_funcs()
init_builtin_types() init_builtin_types()
init_builtin_structs()
global list_type, tuple_type, dict_type, set_type, frozenset_type global list_type, tuple_type, dict_type, set_type, frozenset_type
global bytes_type, str_type, unicode_type global bytes_type, str_type, unicode_type
global float_type, bool_type, type_type, complex_type global float_type, bool_type, type_type, complex_type
......
...@@ -3466,7 +3466,7 @@ class AttributeNode(ExprNode): ...@@ -3466,7 +3466,7 @@ class AttributeNode(ExprNode):
if obj_type.is_ptr or obj_type.is_array: if obj_type.is_ptr or obj_type.is_array:
obj_type = obj_type.base_type obj_type = obj_type.base_type
self.op = "->" self.op = "->"
elif obj_type.is_extension_type: elif obj_type.is_extension_type or obj_type.is_builtin_type:
self.op = "->" self.op = "->"
else: else:
self.op = "." self.op = "."
...@@ -3558,6 +3558,9 @@ class AttributeNode(ExprNode): ...@@ -3558,6 +3558,9 @@ class AttributeNode(ExprNode):
elif obj.type.is_complex: elif obj.type.is_complex:
return "__Pyx_C%s(%s)" % (self.member.upper(), obj_code) return "__Pyx_C%s(%s)" % (self.member.upper(), obj_code)
else: else:
if obj.type.is_builtin_type and self.entry and self.entry.is_variable:
# accessing a field of a builtin type, need to cast better than result_as() does
obj_code = obj.type.cast_code(obj.result(), to_object_struct = True)
return "%s%s%s" % (obj_code, self.op, self.member) return "%s%s%s" % (obj_code, self.op, self.member)
def generate_result_code(self, code): def generate_result_code(self, code):
......
...@@ -373,16 +373,18 @@ class PyObjectType(PyrexType): ...@@ -373,16 +373,18 @@ class PyObjectType(PyrexType):
return cname return cname
class BuiltinObjectType(PyObjectType): class BuiltinObjectType(PyObjectType):
# objstruct_cname string Name of PyObject struct
is_builtin_type = 1 is_builtin_type = 1
has_attributes = 1 has_attributes = 1
base_type = None base_type = None
module_name = '__builtin__' module_name = '__builtin__'
def __init__(self, name, cname): def __init__(self, name, cname, objstruct_cname=None):
self.name = name self.name = name
self.cname = cname self.cname = cname
self.typeptr_cname = "&" + cname self.typeptr_cname = "&" + cname
self.objstruct_cname = objstruct_cname
def set_scope(self, scope): def set_scope(self, scope):
self.scope = scope self.scope = scope
...@@ -445,6 +447,11 @@ class BuiltinObjectType(PyObjectType): ...@@ -445,6 +447,11 @@ class BuiltinObjectType(PyObjectType):
entity_code = "*%s" % entity_code entity_code = "*%s" % entity_code
return self.base_declaration_code(base_code, entity_code) return self.base_declaration_code(base_code, entity_code)
def cast_code(self, expr_code, to_object_struct = False):
return "((%s*)%s)" % (
to_object_struct and self.objstruct_cname or "PyObject", # self.objstruct_cname may be None
expr_code)
class PyExtensionType(PyObjectType): class PyExtensionType(PyObjectType):
# #
......
...@@ -741,9 +741,9 @@ class BuiltinScope(Scope): ...@@ -741,9 +741,9 @@ class BuiltinScope(Scope):
entry.as_variable = var_entry entry.as_variable = var_entry
return entry return entry
def declare_builtin_type(self, name, cname, utility_code = None): def declare_builtin_type(self, name, cname, utility_code = None, objstruct_cname = None):
name = EncodedString(name) name = EncodedString(name)
type = PyrexTypes.BuiltinObjectType(name, cname) type = PyrexTypes.BuiltinObjectType(name, cname, objstruct_cname)
scope = CClassScope(name, outer_scope=None, visibility='extern') scope = CClassScope(name, outer_scope=None, visibility='extern')
scope.directives = {} scope.directives = {}
if name == 'bool': if name == 'bool':
......
cdef int f() except -1:
cdef slice s
cdef object z
cdef int i
z = slice
s = slice(1, 2, 3)
z = slice.indices()
i = s.start
i = s.stop
i = s.step
cimport cython
@cython.test_assert_path_exists('//TupleNode//CoerceToPyTypeNode//AttributeNode')
def complex_attributes():
"""
>>> complex_attributes()
(1.0, 2.0)
"""
cdef complex c = 1+2j
return (c.real, c.imag)
@cython.test_assert_path_exists('//TupleNode//CoerceToPyTypeNode//AttributeNode')
def complex_attributes_assign():
"""
>>> complex_attributes_assign()
(10.0, 20.0)
"""
cdef complex c = 1+2j
c.real, c.imag = 10, 20
return (c.real, c.imag)
@cython.test_assert_path_exists('//TupleNode//CoerceToPyTypeNode//AttributeNode')
def complex_cstruct_assign():
"""
>>> complex_cstruct_assign()
(10.0, 20.0)
"""
cdef complex c = 1+2j
cval = &c.cval
cval.real, cval.imag = 10, 20
return (c.real, c.imag)
cimport cython
def unbound_method_lookup():
"""
>>> unbound_method_lookup()
"""
ignore = slice.indices
@cython.test_assert_path_exists('//SingleAssignmentNode//AttributeNode[@is_py_attr = False]')
@cython.test_fail_if_path_exists('//SingleAssignmentNode//AttributeNode[@is_py_attr = True]')
def typed_slice():
"""
>>> typed_slice()
(1, 2, 3)
"""
cdef slice s
cdef object z
cdef Py_ssize_t a,b,c
z = slice
s = slice(1, 2, 3)
s.indices
a = s.start
b = s.stop
c = s.step
return (a,b,c)
@cython.test_fail_if_path_exists('//SingleAssignmentNode//AttributeNode[@is_py_attr = False]')
def plain_object_slice():
"""
>>> plain_object_slice()
(1, 2, 3)
"""
cdef object s
cdef object z
cdef Py_ssize_t a,b,c
s = slice(1, 2, 3)
s.indices
a = s.start
b = s.stop
c = s.step
return (a,b,c)
...@@ -18,7 +18,6 @@ def test_non_optimised(): ...@@ -18,7 +18,6 @@ def test_non_optimised():
assert isinstance(A(), foo) assert isinstance(A(), foo)
assert isinstance(0, (int, long)) assert isinstance(0, (int, long))
assert not isinstance(u"xyz", (int, long)) assert not isinstance(u"xyz", (int, long))
assert isinstance(complex(), complex) # FIXME: this should be optimised, too!
return True return True
@cython.test_assert_path_exists('//PythonCapiCallNode', @cython.test_assert_path_exists('//PythonCapiCallNode',
...@@ -46,6 +45,7 @@ def test_optimised(): ...@@ -46,6 +45,7 @@ def test_optimised():
assert isinstance(dict(), dict) assert isinstance(dict(), dict)
assert isinstance(set(), set) assert isinstance(set(), set)
assert isinstance(slice(0), slice) assert isinstance(slice(0), slice)
assert isinstance(complex(), complex)
assert not isinstance(u"foo", int) assert not isinstance(u"foo", int)
assert isinstance(A, type) assert isinstance(A, type)
return True return True
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment