def specialize_parent_vtable(cls: ClassIR, parent: ClassIR) -> VTableEntries: """Generate the part of a vtable corresponding to a parent class or trait""" updated = [] for entry in parent.vtable_entries: if isinstance(entry, VTableMethod): # Find the original method corresponding to this vtable entry. # (This may not be the method in the entry, if it was overridden.) orig_parent_method = entry.cls.get_method(entry.name) assert orig_parent_method method_cls = cls.get_method_and_class(entry.name) if method_cls: child_method, defining_cls = method_cls # TODO: emit a wrapper for __init__ that raises or something if (is_same_method_signature(orig_parent_method.sig, child_method.sig) or orig_parent_method.name == '__init__'): entry = VTableMethod(entry.cls, entry.name, child_method, entry.shadow_method) else: entry = VTableMethod(entry.cls, entry.name, defining_cls.glue_methods[(entry.cls, entry.name)], entry.shadow_method) else: # If it is an attribute from a trait, we need to find out # the real class it got mixed in at and point to that. if parent.is_trait: _, origin_cls = cls.attr_details(entry.name) entry = VTableAttr(origin_cls, entry.name, entry.is_setter) updated.append(entry) return updated
def generate_slots(cl: ClassIR, table: SlotTable, emitter: Emitter) -> Dict[str, str]: fields = OrderedDict() # type: Dict[str, str] # Sort for determinism on Python 3.5 for name, (slot, generator) in sorted(table.items()): method_cls = cl.get_method_and_class(name) if method_cls and (method_cls[1] == cl or name in ALWAYS_FILL): fields[slot] = generator(cl, method_cls[0], emitter) return fields
def generate_slots(cl: ClassIR, table: SlotTable, emitter: Emitter) -> Dict[str, str]: fields: Dict[str, str] = OrderedDict() generated: Dict[str, str] = {} # Sort for determinism on Python 3.5 for name, (slot, generator) in sorted(table.items(), key=lambda x: slot_key(x[0])): method_cls = cl.get_method_and_class(name) if method_cls and (method_cls[1] == cl or name in ALWAYS_FILL): if slot in generated: # Reuse previously generated wrapper. fields[slot] = generated[slot] else: # Generate new wrapper. name = generator(cl, method_cls[0], emitter) fields[slot] = name generated[slot] = name return fields
def specialize_parent_vtable(cls: ClassIR, parent: ClassIR) -> VTableEntries: """Generate the part of a vtable corresponding to a parent class or trait""" updated = [] for entry in parent.vtable_entries: # Find the original method corresponding to this vtable entry. # (This may not be the method in the entry, if it was overridden.) orig_parent_method = entry.cls.get_method(entry.name) assert orig_parent_method method_cls = cls.get_method_and_class(entry.name) if method_cls: child_method, defining_cls = method_cls # TODO: emit a wrapper for __init__ that raises or something if (is_same_method_signature(orig_parent_method.sig, child_method.sig) or orig_parent_method.name == '__init__'): entry = VTableMethod(entry.cls, entry.name, child_method, entry.shadow_method) else: entry = VTableMethod( entry.cls, entry.name, defining_cls.glue_methods[(entry.cls, entry.name)], entry.shadow_method) updated.append(entry) return updated
def generate_set_del_item_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str: """Generates a wrapper for native __setitem__ method (also works for __delitem__). This is used with the mapping protocol slot. Arguments are taken as *PyObjects and we return a negative C int on error. Create a separate wrapper function for __delitem__ as needed and have the __setitem__ wrapper call it if the value is NULL. Return the name of the outer (__setitem__) wrapper. """ method_cls = cl.get_method_and_class('__delitem__') del_name = None if method_cls and method_cls[1] == cl: # Generate a separate wrapper for __delitem__ del_name = generate_del_item_wrapper(cl, method_cls[0], emitter) args = fn.args if fn.name == '__delitem__': # Add an extra argument for value that we expect to be NULL. args = list(args) + [RuntimeArg('___value', object_rprimitive, ARG_POS)] name = '{}{}{}'.format(DUNDER_PREFIX, '__setitem__', cl.name_prefix(emitter.names)) input_args = ', '.join('PyObject *obj_{}'.format(arg.name) for arg in args) emitter.emit_line('static int {name}({input_args}) {{'.format( name=name, input_args=input_args, )) # First check if this is __delitem__ emitter.emit_line('if (obj_{} == NULL) {{'.format(args[2].name)) if del_name is not None: # We have a native implementation, so call it emitter.emit_line('return {}(obj_{}, obj_{});'.format(del_name, args[0].name, args[1].name)) else: # Try to call superclass method instead emitter.emit_line( 'PyObject *super = CPy_Super(CPyModule_builtins, obj_{});'.format(args[0].name)) emitter.emit_line('if (super == NULL) return -1;') emitter.emit_line( 'PyObject *result = PyObject_CallMethod(super, "__delitem__", "O", obj_{});'.format( args[1].name)) emitter.emit_line('Py_DECREF(super);') emitter.emit_line('Py_XDECREF(result);') emitter.emit_line('return result == NULL ? -1 : 0;') emitter.emit_line('}') method_cls = cl.get_method_and_class('__setitem__') if method_cls and method_cls[1] == cl: generate_set_del_item_wrapper_inner(fn, emitter, args) else: emitter.emit_line( 'PyObject *super = CPy_Super(CPyModule_builtins, obj_{});'.format(args[0].name)) emitter.emit_line('if (super == NULL) return -1;') emitter.emit_line('PyObject *result;') if method_cls is None and cl.builtin_base is None: msg = "'{}' object does not support item assignment".format(cl.name) emitter.emit_line( 'PyErr_SetString(PyExc_TypeError, "{}");'.format(msg)) emitter.emit_line('result = NULL;') else: # A base class may have __setitem__ emitter.emit_line( 'result = PyObject_CallMethod(super, "__setitem__", "OO", obj_{}, obj_{});'.format( args[1].name, args[2].name)) emitter.emit_line('Py_DECREF(super);') emitter.emit_line('Py_XDECREF(result);') emitter.emit_line('return result == NULL ? -1 : 0;') emitter.emit_line('}') return name