Commit 777b0f32 authored by Damien George's avatar Damien George
Browse files

py: Add property object, with basic functionality.

Enabled by MICROPY_ENABLE_PROPERTY.
parent 4417478d
...@@ -31,6 +31,9 @@ STATIC const mp_map_elem_t mp_builtin_object_table[] = { ...@@ -31,6 +31,9 @@ STATIC const mp_map_elem_t mp_builtin_object_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_list), (mp_obj_t)&mp_type_list }, { MP_OBJ_NEW_QSTR(MP_QSTR_list), (mp_obj_t)&mp_type_list },
{ MP_OBJ_NEW_QSTR(MP_QSTR_map), (mp_obj_t)&mp_type_map }, { MP_OBJ_NEW_QSTR(MP_QSTR_map), (mp_obj_t)&mp_type_map },
{ MP_OBJ_NEW_QSTR(MP_QSTR_object), (mp_obj_t)&mp_type_object }, { MP_OBJ_NEW_QSTR(MP_QSTR_object), (mp_obj_t)&mp_type_object },
#if MICROPY_ENABLE_PROPERTY
{ MP_OBJ_NEW_QSTR(MP_QSTR_property), (mp_obj_t)&mp_type_property },
#endif
{ MP_OBJ_NEW_QSTR(MP_QSTR_set), (mp_obj_t)&mp_type_set }, { MP_OBJ_NEW_QSTR(MP_QSTR_set), (mp_obj_t)&mp_type_set },
{ MP_OBJ_NEW_QSTR(MP_QSTR_str), (mp_obj_t)&mp_type_str }, { MP_OBJ_NEW_QSTR(MP_QSTR_str), (mp_obj_t)&mp_type_str },
{ MP_OBJ_NEW_QSTR(MP_QSTR_super), (mp_obj_t)&mp_type_super }, { MP_OBJ_NEW_QSTR(MP_QSTR_super), (mp_obj_t)&mp_type_super },
......
...@@ -135,6 +135,11 @@ typedef double mp_float_t; ...@@ -135,6 +135,11 @@ typedef double mp_float_t;
#define MICROPY_ENABLE_SLICE (1) #define MICROPY_ENABLE_SLICE (1)
#endif #endif
// Whether to support the property object
#ifndef MICROPY_ENABLE_PROPERTY
#define MICROPY_ENABLE_PROPERTY (0)
#endif
// Enable features which improve CPython compatibility // Enable features which improve CPython compatibility
// but may lead to more code size/memory usage. // but may lead to more code size/memory usage.
// TODO: Originally intended as generic category to not // TODO: Originally intended as generic category to not
......
...@@ -283,6 +283,7 @@ extern const mp_obj_type_t mp_type_fun_bc; ...@@ -283,6 +283,7 @@ extern const mp_obj_type_t mp_type_fun_bc;
extern const mp_obj_type_t mp_type_module; extern const mp_obj_type_t mp_type_module;
extern const mp_obj_type_t mp_type_staticmethod; extern const mp_obj_type_t mp_type_staticmethod;
extern const mp_obj_type_t mp_type_classmethod; extern const mp_obj_type_t mp_type_classmethod;
extern const mp_obj_type_t mp_type_property;
// Exceptions // Exceptions
extern const mp_obj_type_t mp_type_BaseException; extern const mp_obj_type_t mp_type_BaseException;
...@@ -518,6 +519,9 @@ typedef struct _mp_obj_static_class_method_t { ...@@ -518,6 +519,9 @@ typedef struct _mp_obj_static_class_method_t {
mp_obj_t fun; mp_obj_t fun;
} mp_obj_static_class_method_t; } mp_obj_static_class_method_t;
// property
const mp_obj_t *mp_obj_property_get(mp_obj_t self_in);
// sequence helpers // sequence helpers
void mp_seq_multiply(const void *items, uint item_sz, uint len, uint times, void *dest); void mp_seq_multiply(const void *items, uint item_sz, uint len, uint times, void *dest);
bool m_seq_get_fast_slice_indexes(machine_uint_t len, mp_obj_t slice, machine_uint_t *begin, machine_uint_t *end); bool m_seq_get_fast_slice_indexes(machine_uint_t len, mp_obj_t slice, machine_uint_t *begin, machine_uint_t *end);
......
#include <stdlib.h>
#include <assert.h>
#include "nlr.h"
#include "misc.h"
#include "mpconfig.h"
#include "qstr.h"
#include "obj.h"
#include "runtime.h"
#if MICROPY_ENABLE_PROPERTY
typedef struct _mp_obj_property_t {
mp_obj_base_t base;
mp_obj_t proxy[3]; // getter, setter, deleter
} mp_obj_property_t;
STATIC mp_obj_t property_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
// TODO check n_kw == 0
mp_obj_property_t *o = m_new_obj(mp_obj_property_t);
o->base.type = &mp_type_property;
if (n_args >= 5) {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "property takes at most 4 arguments"));
}
if (n_args >= 4) {
// doc ignored
}
if (n_args >= 3) {
o->proxy[2] = args[2];
} else {
o->proxy[2] = mp_const_none;
}
if (n_args >= 2) {
o->proxy[1] = args[1];
} else {
o->proxy[1] = mp_const_none;
}
if (n_args >= 1) {
o->proxy[0] = args[0];
} else {
o->proxy[0] = mp_const_none;
}
return o;
}
STATIC mp_obj_t property_getter(mp_obj_t self_in, mp_obj_t getter) {
mp_obj_property_t *p2 = m_new_obj(mp_obj_property_t);
*p2 = *(mp_obj_property_t*)self_in;
p2->proxy[0] = getter;
return p2;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(property_getter_obj, property_getter);
STATIC mp_obj_t property_setter(mp_obj_t self_in, mp_obj_t setter) {
mp_obj_property_t *p2 = m_new_obj(mp_obj_property_t);
*p2 = *(mp_obj_property_t*)self_in;
p2->proxy[1] = setter;
return p2;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(property_setter_obj, property_setter);
STATIC mp_obj_t property_deleter(mp_obj_t self_in, mp_obj_t deleter) {
mp_obj_property_t *p2 = m_new_obj(mp_obj_property_t);
*p2 = *(mp_obj_property_t*)self_in;
p2->proxy[2] = deleter;
return p2;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(property_deleter_obj, property_deleter);
STATIC const mp_map_elem_t property_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_getter), (mp_obj_t)&property_getter_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_setter), (mp_obj_t)&property_setter_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_deleter), (mp_obj_t)&property_deleter_obj },
};
STATIC MP_DEFINE_CONST_DICT(property_locals_dict, property_locals_dict_table);
const mp_obj_type_t mp_type_property = {
{ &mp_type_type },
.name = MP_QSTR_property,
.make_new = property_make_new,
.locals_dict = (mp_obj_t)&property_locals_dict,
};
const mp_obj_t *mp_obj_property_get(mp_obj_t self_in) {
assert(MP_OBJ_IS_TYPE(self_in, &mp_type_property));
mp_obj_property_t *self = self_in;
return self->proxy;
}
#endif // MICROPY_ENABLE_PROPERTY
...@@ -229,15 +229,35 @@ STATIC mp_obj_t class_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) { ...@@ -229,15 +229,35 @@ STATIC mp_obj_t class_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
STATIC void class_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { STATIC void class_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
// logic: look in obj members then class locals (TODO check this against CPython) // logic: look in obj members then class locals (TODO check this against CPython)
mp_obj_class_t *self = self_in; mp_obj_class_t *self = self_in;
mp_map_elem_t *elem = mp_map_lookup(&self->members, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); mp_map_elem_t *elem = mp_map_lookup(&self->members, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP);
if (elem != NULL) { if (elem != NULL) {
// object member, always treated as a value // object member, always treated as a value
// TODO should we check for properties?
dest[0] = elem->value; dest[0] = elem->value;
return; return;
} }
mp_obj_t member = mp_obj_class_lookup(self->base.type, attr); mp_obj_t member = mp_obj_class_lookup(self->base.type, attr);
if (member != MP_OBJ_NULL) { if (member != MP_OBJ_NULL) {
class_convert_return_attr(self_in, member, dest); if (0) {
#if MICROPY_ENABLE_PROPERTY
} else if (MP_OBJ_IS_TYPE(member, &mp_type_property)) {
// object member is a property
// delegate the store to the property
// TODO should this be part of class_convert_return_attr?
const mp_obj_t *proxy = mp_obj_property_get(member);
if (proxy[0] == mp_const_none) {
// TODO
} else {
dest[0] = mp_call_function_n_kw(proxy[0], 1, 0, &self_in);
// TODO should we convert the returned value using class_convert_return_attr?
}
#endif
} else {
// not a property
class_convert_return_attr(self_in, member, dest);
}
return; return;
} }
...@@ -257,10 +277,30 @@ STATIC void class_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { ...@@ -257,10 +277,30 @@ STATIC void class_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
STATIC bool class_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) { STATIC bool class_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) {
mp_obj_class_t *self = self_in; mp_obj_class_t *self = self_in;
#if MICROPY_ENABLE_PROPERTY
// for property, we need to do a lookup first in the class dict
// this makes all stores slow... how to fix?
mp_obj_t member = mp_obj_class_lookup(self->base.type, attr);
if (member != MP_OBJ_NULL && MP_OBJ_IS_TYPE(member, &mp_type_property)) {
// attribute already exists and is a property
// delegate the store to the property
const mp_obj_t *proxy = mp_obj_property_get(member);
if (proxy[1] == mp_const_none) {
// TODO better error message
return false;
} else {
mp_obj_t dest[2] = {self_in, value};
mp_call_function_n_kw(proxy[1], 2, 0, dest);
return true;
}
}
#endif
if (value == MP_OBJ_NULL) { if (value == MP_OBJ_NULL) {
// delete attribute // delete attribute
mp_map_elem_t *el = mp_map_lookup(&self->members, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_REMOVE_IF_FOUND); mp_map_elem_t *elem = mp_map_lookup(&self->members, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_REMOVE_IF_FOUND);
return el != NULL; return elem != NULL;
} else { } else {
// store attribute // store attribute
mp_map_lookup(&self->members, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value; mp_map_lookup(&self->members, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
......
...@@ -60,6 +60,7 @@ PY_O_BASENAME = \ ...@@ -60,6 +60,7 @@ PY_O_BASENAME = \
objmap.o \ objmap.o \
objmodule.o \ objmodule.o \
objobject.o \ objobject.o \
objproperty.o \
objnone.o \ objnone.o \
objnamedtuple.o \ objnamedtuple.o \
objrange.o \ objrange.o \
......
...@@ -281,3 +281,10 @@ Q(stdout) ...@@ -281,3 +281,10 @@ Q(stdout)
Q(stderr) Q(stderr)
Q(version_info) Q(version_info)
#endif #endif
#if MICROPY_ENABLE_PROPERTY
Q(property)
Q(getter)
Q(setter)
Q(deleter)
#endif
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#define MICROPY_ENABLE_REPL_HELPERS (1) #define MICROPY_ENABLE_REPL_HELPERS (1)
#define MICROPY_ENABLE_LEXER_UNIX (1) #define MICROPY_ENABLE_LEXER_UNIX (1)
#define MICROPY_ENABLE_SOURCE_LINE (1) #define MICROPY_ENABLE_SOURCE_LINE (1)
#define MICROPY_ENABLE_PROPERTY (1)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
#define MICROPY_PATH_MAX (PATH_MAX) #define MICROPY_PATH_MAX (PATH_MAX)
......
Supports Markdown
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