objinstance.c 3.59 KB
Newer Older
1
2
3
4
5
6
7
8
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>

#include "nlr.h"
#include "misc.h"
#include "mpconfig.h"
9
#include "mpqstr.h"
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include "obj.h"
#include "runtime.h"
#include "map.h"

typedef struct _mp_obj_instance_t {
    mp_obj_base_t base;
    mp_obj_base_t *class; // points to a "class" object
    mp_map_t *members;
} mp_obj_instance_t;

/*
type needs to be specified dynamically
            case O_OBJ:
            {
                py_map_elem_t *qn = py_qstr_map_lookup(o->u_obj.class->u_class.locals, qstr_from_str_static("__qualname__"), false); assert(qn != NULL);
                assert(IS_O(qn->value, O_STR));
                return qstr_str(((py_obj_base_t*)qn->value)->u_str);
            }
            */

mp_obj_t mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr) {
    // logic: look in obj members then class locals (TODO check this against CPython)
    mp_obj_instance_t *self = self_in;
    mp_map_elem_t *elem = mp_qstr_map_lookup(self->members, attr, false);
    if (elem != NULL) {
        // object member, always treated as a value
        return elem->value;
    }
    elem = mp_qstr_map_lookup(mp_obj_class_get_locals(self->class), attr, false);
    if (elem != NULL) {
        if (mp_obj_is_callable(elem->value)) {
            // class member is callable so build a bound method
            return mp_obj_new_bound_meth(self_in, elem->value);
        } else {
            // class member is a value, so just return that value
            return elem->value;
        }
    }
48
    nlr_jump(mp_obj_new_exception_msg_2_args(MP_QSTR_AttributeError, "'%s' object has no attribute '%s'", mp_obj_get_type_str(self_in), qstr_str(attr)));
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
}

void mp_obj_instance_load_method(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
    // logic: look in obj members then class locals (TODO check this against CPython)
    mp_obj_instance_t *self = self_in;
    mp_map_elem_t *elem = mp_qstr_map_lookup(self->members, attr, false);
    if (elem != NULL) {
        // object member, always treated as a value
        dest[1] = elem->value;
        dest[0] = NULL;
        return;
    }
    elem = mp_qstr_map_lookup(mp_obj_class_get_locals(self->class), attr, false);
    if (elem != NULL) {
        if (mp_obj_is_callable(elem->value)) {
            // class member is callable so build a bound method
            dest[1] = elem->value;
            dest[0] = self_in;
            return;
        } else {
            // class member is a value, so just return that value
            dest[1] = elem->value;
            dest[0] = NULL;
            return;
        }
    }

    // no such method, so fall back to load attr
    dest[1] = rt_load_attr(self_in, attr);
    dest[0] = NULL;
}

void mp_obj_instance_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) {
    // logic: look in class locals (no add) then obj members (add) (TODO check this against CPython)
    mp_obj_instance_t *self = self_in;
    mp_map_elem_t *elem = mp_qstr_map_lookup(mp_obj_class_get_locals(self->class), attr, false);
    if (elem != NULL) {
        elem->value = value;
    } else {
        mp_qstr_map_lookup(self->members, attr, true)->value = value;
    }
}

const mp_obj_type_t instance_type = {
    { &mp_const_type },
    "instance",
    NULL, // print
96
    NULL, // make_new
97
98
99
100
101
    NULL, // call_n
    NULL, // unary_op
    NULL, // binary_op
    NULL, // getiter
    NULL, // iternext
102
    .methods = {{NULL, NULL},},
103
104
105
106
107
108
109
110
111
};

mp_obj_t mp_obj_new_instance(mp_obj_t class) {
    mp_obj_instance_t *o = m_new_obj(mp_obj_instance_t);
    o->base.type = &instance_type;
    o->class = class;
    o->members = mp_map_new(MP_MAP_QSTR, 0);
    return o;
}