From bfc2092dc55d1faab4a6d7e832005afd61d25c3c Mon Sep 17 00:00:00 2001
From: Paul Sokolovsky <pfalcon@users.sourceforge.net>
Date: Fri, 11 Aug 2017 09:42:39 +0300
Subject: [PATCH] py/modsys: Initial implementation of sys.getsizeof().

Implemented as a new MP_UNARY_OP. This patch adds support lists, dicts and
instances.
---
 py/modsys.c                      | 12 ++++++++++++
 py/mpconfig.h                    |  5 +++++
 py/objdict.c                     |  6 ++++++
 py/objlist.c                     |  6 ++++++
 py/objtype.c                     | 16 ++++++++++++++++
 py/runtime0.h                    |  1 +
 tests/unix/extra_coverage.py.exp |  3 ++-
 unix/mpconfigport_coverage.h     |  1 +
 8 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/py/modsys.c b/py/modsys.c
index ee6f8686e..b7d55fdcc 100644
--- a/py/modsys.c
+++ b/py/modsys.c
@@ -4,6 +4,7 @@
  * The MIT License (MIT)
  *
  * Copyright (c) 2013, 2014 Damien P. George
+ * Copyright (c) 2014-2017 Paul Sokolovsky
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -31,8 +32,11 @@
 #include "py/objtuple.h"
 #include "py/objstr.h"
 #include "py/objint.h"
+#include "py/objtype.h"
 #include "py/stream.h"
 #include "py/smallint.h"
+#include "py/runtime0.h"
+#include "py/runtime.h"
 
 #if MICROPY_PY_SYS
 
@@ -143,6 +147,11 @@ STATIC mp_obj_t mp_sys_exc_info(void) {
 MP_DEFINE_CONST_FUN_OBJ_0(mp_sys_exc_info_obj, mp_sys_exc_info);
 #endif
 
+STATIC mp_obj_t mp_sys_getsizeof(mp_obj_t obj) {
+    return mp_unary_op(MP_UNARY_OP_SIZEOF, obj);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_getsizeof_obj, mp_sys_getsizeof);
+
 STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys) },
 
@@ -192,6 +201,9 @@ STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = {
     #if MICROPY_PY_SYS_EXC_INFO
     { MP_ROM_QSTR(MP_QSTR_exc_info), MP_ROM_PTR(&mp_sys_exc_info_obj) },
     #endif
+    #if MICROPY_PY_SYS_GETSIZEOF
+    { MP_ROM_QSTR(MP_QSTR_getsizeof), MP_ROM_PTR(&mp_sys_getsizeof_obj) },
+    #endif
 
     /*
      * Extensions to CPython
diff --git a/py/mpconfig.h b/py/mpconfig.h
index fb507a503..0e76a1ff5 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -944,6 +944,11 @@ typedef double mp_float_t;
 #define MICROPY_PY_SYS_EXIT (1)
 #endif
 
+// Whether to provide "sys.getsizeof" function
+#ifndef MICROPY_PY_SYS_GETSIZEOF
+#define MICROPY_PY_SYS_GETSIZEOF (0)
+#endif
+
 // Whether to provide sys.{stdin,stdout,stderr} objects
 #ifndef MICROPY_PY_SYS_STDFILES
 #define MICROPY_PY_SYS_STDFILES (0)
diff --git a/py/objdict.c b/py/objdict.c
index a272ebdb6..f6357a905 100644
--- a/py/objdict.c
+++ b/py/objdict.c
@@ -105,6 +105,12 @@ STATIC mp_obj_t dict_unary_op(mp_uint_t op, mp_obj_t self_in) {
     switch (op) {
         case MP_UNARY_OP_BOOL: return mp_obj_new_bool(self->map.used != 0);
         case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->map.used);
+        #if MICROPY_PY_SYS_GETSIZEOF
+        case MP_UNARY_OP_SIZEOF: {
+            size_t sz = sizeof(*self) + sizeof(*self->map.table) * self->map.alloc;
+            return MP_OBJ_NEW_SMALL_INT(sz);
+        }
+        #endif
         default: return MP_OBJ_NULL; // op not supported
     }
 }
diff --git a/py/objlist.c b/py/objlist.c
index ba1c50677..5957c9d4a 100644
--- a/py/objlist.c
+++ b/py/objlist.c
@@ -104,6 +104,12 @@ STATIC mp_obj_t list_unary_op(mp_uint_t op, mp_obj_t self_in) {
     switch (op) {
         case MP_UNARY_OP_BOOL: return mp_obj_new_bool(self->len != 0);
         case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(self->len);
+        #if MICROPY_PY_SYS_GETSIZEOF
+        case MP_UNARY_OP_SIZEOF: {
+            size_t sz = sizeof(*self) + sizeof(mp_obj_t) * self->alloc;
+            return MP_OBJ_NEW_SMALL_INT(sz);
+        }
+        #endif
         default: return MP_OBJ_NULL; // op not supported
     }
 }
diff --git a/py/objtype.c b/py/objtype.c
index a258915f3..87c0cc9e5 100644
--- a/py/objtype.c
+++ b/py/objtype.c
@@ -339,11 +339,27 @@ const qstr mp_unary_op_method_name[] = {
     [MP_UNARY_OP_NEGATIVE] = MP_QSTR___neg__,
     [MP_UNARY_OP_INVERT] = MP_QSTR___invert__,
     #endif
+    #if MICROPY_PY_SYS_GETSIZEOF
+    [MP_UNARY_OP_SIZEOF] = MP_QSTR_getsizeof,
+    #endif
     [MP_UNARY_OP_NOT] = MP_QSTR_, // don't need to implement this, used to make sure array has full size
 };
 
 STATIC mp_obj_t instance_unary_op(mp_uint_t op, mp_obj_t self_in) {
     mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in);
+
+    #if MICROPY_PY_SYS_GETSIZEOF
+    if (MP_UNLIKELY(op == MP_UNARY_OP_SIZEOF)) {
+        // TODO: This doesn't count inherited objects (self->subobj)
+        const mp_obj_type_t *native_base;
+        size_t num_native_bases = instance_count_native_bases(mp_obj_get_type(self_in), &native_base);
+
+        size_t sz = sizeof(*self) + sizeof(*self->subobj) * num_native_bases
+            + sizeof(*self->members.table) * self->members.alloc;
+        return MP_OBJ_NEW_SMALL_INT(sz);
+    }
+    #endif
+
     qstr op_name = mp_unary_op_method_name[op];
     /* Still try to lookup native slot
     if (op_name == 0) {
diff --git a/py/runtime0.h b/py/runtime0.h
index d22c2fabe..703c950f2 100644
--- a/py/runtime0.h
+++ b/py/runtime0.h
@@ -50,6 +50,7 @@ typedef enum {
     MP_UNARY_OP_NEGATIVE,
     MP_UNARY_OP_INVERT,
     MP_UNARY_OP_NOT,
+    MP_UNARY_OP_SIZEOF, // for sys.getsizeof()
 } mp_unary_op_t;
 
 typedef enum {
diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp
index 390ff1669..ab638a632 100644
--- a/tests/unix/extra_coverage.py.exp
+++ b/tests/unix/extra_coverage.py.exp
@@ -25,7 +25,8 @@ ame__
 __name__        path            argv            version
 version_info    implementation  platform        byteorder
 maxsize         exit            stdin           stdout
-stderr          modules         exc_info        print_exception
+stderr          modules         exc_info        getsizeof
+print_exception
 ementation
 # attrtuple
 (start=1, stop=2, step=3)
diff --git a/unix/mpconfigport_coverage.h b/unix/mpconfigport_coverage.h
index 5fc8d7107..a9e0a38fa 100644
--- a/unix/mpconfigport_coverage.h
+++ b/unix/mpconfigport_coverage.h
@@ -37,6 +37,7 @@
 #define MICROPY_PY_DELATTR_SETATTR     (1)
 #define MICROPY_PY_BUILTINS_HELP       (1)
 #define MICROPY_PY_BUILTINS_HELP_MODULES (1)
+#define MICROPY_PY_SYS_GETSIZEOF       (1)
 #define MICROPY_PY_URANDOM_EXTRA_FUNCS (1)
 #define MICROPY_PY_IO_BUFFEREDWRITER (1)
 #undef MICROPY_VFS_FAT
-- 
GitLab