66
77from mypyc .common import PREFIX , NATIVE_PREFIX , REG_PREFIX
88from mypyc .codegen .emit import Emitter , HeaderDeclaration
9- from mypyc .codegen .emitfunc import native_function_header , native_getter_name , native_setter_name
9+ from mypyc .codegen .emitfunc import native_function_header
1010from mypyc .codegen .emitwrapper import (
1111 generate_dunder_wrapper , generate_hash_wrapper , generate_richcompare_wrapper ,
1212 generate_bool_wrapper , generate_get_wrapper ,
1313)
1414from mypyc .ir .rtypes import RType , RTuple , object_rprimitive
1515from mypyc .ir .func_ir import FuncIR , FuncDecl , FUNC_STATICMETHOD , FUNC_CLASSMETHOD
16- from mypyc .ir .class_ir import ClassIR , VTableMethod , VTableEntries
16+ from mypyc .ir .class_ir import ClassIR , VTableEntries
1717from mypyc .sametype import is_same_type
1818from mypyc .namegen import NameGenerator
1919
@@ -98,8 +98,6 @@ def generate_class_type_decl(cl: ClassIR, c_emitter: Emitter,
9898 generate_object_struct (cl , external_emitter )
9999 generate_full = not cl .is_trait and not cl .builtin_base
100100 if generate_full :
101- declare_native_getters_and_setters (cl , emitter )
102-
103101 context .declarations [emitter .native_function_name (cl .ctor )] = HeaderDeclaration (
104102 '{};' .format (native_function_header (cl .ctor , emitter )),
105103 needs_export = True ,
@@ -216,7 +214,6 @@ def emit_line() -> None:
216214 emit_line ()
217215 generate_dealloc_for_class (cl , dealloc_name , clear_name , emitter )
218216 emit_line ()
219- generate_native_getters_and_setters (cl , emitter )
220217
221218 if cl .allow_interpreted_subclasses :
222219 shadow_vtable_name = generate_vtables (
@@ -293,64 +290,6 @@ def generate_object_struct(cl: ClassIR, emitter: Emitter) -> None:
293290 )
294291
295292
296- def declare_native_getters_and_setters (cl : ClassIR ,
297- emitter : Emitter ) -> None :
298- decls = emitter .context .declarations
299- for attr , rtype in cl .attributes .items ():
300- getter_name = native_getter_name (cl , attr , emitter .names )
301- setter_name = native_setter_name (cl , attr , emitter .names )
302- decls [getter_name ] = HeaderDeclaration (
303- '{}{}({} *self);' .format (emitter .ctype_spaced (rtype ),
304- getter_name ,
305- cl .struct_name (emitter .names )),
306- needs_export = True ,
307- )
308- decls [setter_name ] = HeaderDeclaration (
309- 'bool {}({} *self, {}value);' .format (native_setter_name (cl , attr , emitter .names ),
310- cl .struct_name (emitter .names ),
311- emitter .ctype_spaced (rtype )),
312- needs_export = True ,
313- )
314-
315-
316- def generate_native_getters_and_setters (cl : ClassIR ,
317- emitter : Emitter ) -> None :
318- for attr , rtype in cl .attributes .items ():
319- attr_field = emitter .attr (attr )
320-
321- # Native getter
322- emitter .emit_line ('{}{}({} *self)' .format (emitter .ctype_spaced (rtype ),
323- native_getter_name (cl , attr , emitter .names ),
324- cl .struct_name (emitter .names )))
325- emitter .emit_line ('{' )
326- if rtype .is_refcounted :
327- emit_undefined_check (rtype , emitter , attr_field , '==' )
328- emitter .emit_lines (
329- 'PyErr_SetString(PyExc_AttributeError, "attribute {} of {} undefined");' .format (
330- repr (attr ), repr (cl .name )),
331- '} else {' )
332- emitter .emit_inc_ref ('self->{}' .format (attr_field ), rtype )
333- emitter .emit_line ('}' )
334- emitter .emit_line ('return self->{};' .format (attr_field ))
335- emitter .emit_line ('}' )
336- emitter .emit_line ()
337- # Native setter
338- emitter .emit_line (
339- 'bool {}({} *self, {}value)' .format (native_setter_name (cl , attr , emitter .names ),
340- cl .struct_name (emitter .names ),
341- emitter .ctype_spaced (rtype )))
342- emitter .emit_line ('{' )
343- if rtype .is_refcounted :
344- emit_undefined_check (rtype , emitter , attr_field , '!=' )
345- emitter .emit_dec_ref ('self->{}' .format (attr_field ), rtype )
346- emitter .emit_line ('}' )
347- # This steal the reference to src, so we don't need to increment the arg
348- emitter .emit_lines ('self->{} = value;' .format (attr_field ),
349- 'return 1;' ,
350- '}' )
351- emitter .emit_line ()
352-
353-
354293def generate_vtables (base : ClassIR ,
355294 vtable_setup_name : str ,
356295 vtable_name : str ,
@@ -359,6 +298,18 @@ def generate_vtables(base: ClassIR,
359298 """Emit the vtables and vtable setup functions for a class.
360299
361300 This includes both the primary vtable and any trait implementation vtables.
301+ The trait vtables go before the main vtable, and have the following layout:
302+ {
303+ CPyType_T1, // pointer to type object
304+ C_T1_trait_vtable, // pointer to array of method pointers
305+ C_T1_offset_table, // pointer to array of attribute offsets
306+ CPyType_T2,
307+ C_T2_trait_vtable,
308+ C_T2_offset_table,
309+ ...
310+ }
311+ The method implementations are calculated at the end of IR pass, attribute
312+ offsets are {offsetof(native__C, _x1), offsetof(native__C, _y1), ...}.
362313
363314 To account for both dynamic loading and dynamic class creation,
364315 vtables are populated dynamically at class creation time, so we
@@ -370,22 +321,33 @@ def generate_vtables(base: ClassIR,
370321
371322 Returns the expression to use to refer to the vtable, which might be
372323 different than the name, if there are trait vtables.
373-
374324 """
375325
376326 def trait_vtable_name (trait : ClassIR ) -> str :
377327 return '{}_{}_trait_vtable{}' .format (
378328 base .name_prefix (emitter .names ), trait .name_prefix (emitter .names ),
379329 '_shadow' if shadow else '' )
380330
331+ def trait_offset_table_name (trait : ClassIR ) -> str :
332+ return '{}_{}_offset_table' .format (
333+ base .name_prefix (emitter .names ), trait .name_prefix (emitter .names )
334+ )
335+
381336 # Emit array definitions with enough space for all the entries
382337 emitter .emit_line ('static CPyVTableItem {}[{}];' .format (
383338 vtable_name ,
384- max (1 , len (base .vtable_entries ) + 2 * len (base .trait_vtables ))))
339+ max (1 , len (base .vtable_entries ) + 3 * len (base .trait_vtables ))))
340+
385341 for trait , vtable in base .trait_vtables .items ():
342+ # Trait methods entry (vtable index -> method implementation).
386343 emitter .emit_line ('static CPyVTableItem {}[{}];' .format (
387344 trait_vtable_name (trait ),
388345 max (1 , len (vtable ))))
346+ # Trait attributes entry (attribute number in trait -> offset in actual struct).
347+ emitter .emit_line ('static size_t {}[{}];' .format (
348+ trait_offset_table_name (trait ),
349+ max (1 , len (trait .attributes )))
350+ )
389351
390352 # Emit vtable setup function
391353 emitter .emit_line ('static bool' )
@@ -398,43 +360,59 @@ def trait_vtable_name(trait: ClassIR) -> str:
398360 subtables = []
399361 for trait , vtable in base .trait_vtables .items ():
400362 name = trait_vtable_name (trait )
363+ offset_name = trait_offset_table_name (trait )
401364 generate_vtable (vtable , name , emitter , [], shadow )
402- subtables .append ((trait , name ))
365+ generate_offset_table (offset_name , emitter , trait , base )
366+ subtables .append ((trait , name , offset_name ))
403367
404368 generate_vtable (base .vtable_entries , vtable_name , emitter , subtables , shadow )
405369
406370 emitter .emit_line ('return 1;' )
407371 emitter .emit_line ('}' )
408372
409- return vtable_name if not subtables else "{} + {}" .format (vtable_name , len (subtables ) * 2 )
373+ return vtable_name if not subtables else "{} + {}" .format (vtable_name , len (subtables ) * 3 )
374+
375+
376+ def generate_offset_table (trait_offset_table_name : str ,
377+ emitter : Emitter ,
378+ trait : ClassIR ,
379+ cl : ClassIR ) -> None :
380+ """Generate attribute offset row of a trait vtable."""
381+ emitter .emit_line ('size_t {}_scratch[] = {{' .format (trait_offset_table_name ))
382+ for attr in trait .attributes :
383+ emitter .emit_line ('offsetof({}, {}),' .format (
384+ cl .struct_name (emitter .names ), emitter .attr (attr )
385+ ))
386+ if not trait .attributes :
387+ # This is for msvc.
388+ emitter .emit_line ('0' )
389+ emitter .emit_line ('};' )
390+ emitter .emit_line ('memcpy({name}, {name}_scratch, sizeof({name}));' .format (
391+ name = trait_offset_table_name )
392+ )
410393
411394
412395def generate_vtable (entries : VTableEntries ,
413396 vtable_name : str ,
414397 emitter : Emitter ,
415- subtables : List [Tuple [ClassIR , str ]],
398+ subtables : List [Tuple [ClassIR , str , str ]],
416399 shadow : bool ) -> None :
417400 emitter .emit_line ('CPyVTableItem {}_scratch[] = {{' .format (vtable_name ))
418401 if subtables :
419402 emitter .emit_line ('/* Array of trait vtables */' )
420- for trait , table in subtables :
421- emitter .emit_line ('(CPyVTableItem){}, (CPyVTableItem){},' .format (
422- emitter .type_struct_name (trait ), table ))
403+ for trait , table , offset_table in subtables :
404+ emitter .emit_line (
405+ '(CPyVTableItem){}, (CPyVTableItem){}, (CPyVTableItem){},' .format (
406+ emitter .type_struct_name (trait ), table , offset_table ))
423407 emitter .emit_line ('/* Start of real vtable */' )
424408
425409 for entry in entries :
426- if isinstance (entry , VTableMethod ):
427- method = entry .shadow_method if shadow and entry .shadow_method else entry .method
428- emitter .emit_line ('(CPyVTableItem){}{}{},' .format (
429- emitter .get_group_prefix (entry .method .decl ),
430- NATIVE_PREFIX ,
431- method .cname (emitter .names )))
432- else :
433- cl , attr , is_setter = entry
434- namer = native_setter_name if is_setter else native_getter_name
435- emitter .emit_line ('(CPyVTableItem){}{},' .format (
436- emitter .get_group_prefix (cl ),
437- namer (cl , attr , emitter .names )))
410+ method = entry .shadow_method if shadow and entry .shadow_method else entry .method
411+ emitter .emit_line ('(CPyVTableItem){}{}{},' .format (
412+ emitter .get_group_prefix (entry .method .decl ),
413+ NATIVE_PREFIX ,
414+ method .cname (emitter .names )))
415+
438416 # msvc doesn't allow empty arrays; maybe allowing them at all is an extension?
439417 if not entries :
440418 emitter .emit_line ('NULL' )
@@ -747,7 +725,8 @@ def generate_getter(cl: ClassIR,
747725 emitter .emit_line ('{}({} *self, void *closure)' .format (getter_name (cl , attr , emitter .names ),
748726 cl .struct_name (emitter .names )))
749727 emitter .emit_line ('{' )
750- emit_undefined_check (rtype , emitter , attr_field , '==' )
728+ attr_expr = 'self->{}' .format (attr_field )
729+ emitter .emit_undefined_attr_check (rtype , attr_expr , '==' , unlikely = True )
751730 emitter .emit_line ('PyErr_SetString(PyExc_AttributeError,' )
752731 emitter .emit_line (' "attribute {} of {} undefined");' .format (repr (attr ),
753732 repr (cl .name )))
@@ -770,7 +749,8 @@ def generate_setter(cl: ClassIR,
770749 cl .struct_name (emitter .names )))
771750 emitter .emit_line ('{' )
772751 if rtype .is_refcounted :
773- emit_undefined_check (rtype , emitter , attr_field , '!=' )
752+ attr_expr = 'self->{}' .format (attr_field )
753+ emitter .emit_undefined_attr_check (rtype , attr_expr , '!=' )
774754 emitter .emit_dec_ref ('self->{}' .format (attr_field ), rtype )
775755 emitter .emit_line ('}' )
776756 emitter .emit_line ('if (value != NULL) {' )
@@ -833,15 +813,3 @@ def generate_property_setter(cl: ClassIR,
833813 func_ir .cname (emitter .names )))
834814 emitter .emit_line ('return 0;' )
835815 emitter .emit_line ('}' )
836-
837-
838- def emit_undefined_check (rtype : RType , emitter : Emitter , attr : str , compare : str ) -> None :
839- if isinstance (rtype , RTuple ):
840- attr_expr = 'self->{}' .format (attr )
841- emitter .emit_line (
842- 'if ({}) {{' .format (
843- emitter .tuple_undefined_check_cond (
844- rtype , attr_expr , emitter .c_undefined_value , compare )))
845- else :
846- emitter .emit_line (
847- 'if (self->{} {} {}) {{' .format (attr , compare , emitter .c_undefined_value (rtype )))
0 commit comments