py/objobject: Add object.__setattr__ function.

Allows assigning attributes on class instances that implement their own
__setattr__.  Both object.__setattr__ and super(A, b).__setattr__ will work
with this commit.
diff --git a/py/objobject.c b/py/objobject.c
index 4522834..ae8436b 100644
--- a/py/objobject.c
+++ b/py/objobject.c
@@ -50,7 +50,7 @@
 
 STATIC mp_obj_t object___new__(mp_obj_t cls) {
     if (!mp_obj_is_type(cls, &mp_type_type) || !mp_obj_is_instance_type((mp_obj_type_t*)MP_OBJ_TO_PTR(cls))) {
-        mp_raise_TypeError("__new__ arg must be a user-type");
+        mp_raise_TypeError("arg must be user-type");
     }
     // This executes only "__new__" part of instance creation.
     // TODO: This won't work well for classes with native bases.
@@ -62,6 +62,23 @@
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(object___new___fun_obj, object___new__);
 STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(object___new___obj, MP_ROM_PTR(&object___new___fun_obj));
 
+#if MICROPY_PY_DELATTR_SETATTR
+STATIC mp_obj_t object___setattr__(mp_obj_t self_in, mp_obj_t attr, mp_obj_t value) {
+    if (!mp_obj_is_instance_type(mp_obj_get_type(MP_OBJ_TO_PTR(self_in)))) {
+        mp_raise_TypeError("arg must be user-type");
+    }
+
+    if (!mp_obj_is_str(attr)) {
+        mp_raise_TypeError(NULL);
+    }
+
+    mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in);
+    mp_map_lookup(&self->members, attr, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(object___setattr___obj, object___setattr__);
+#endif
+
 STATIC const mp_rom_map_elem_t object_locals_dict_table[] = {
     #if MICROPY_CPYTHON_COMPAT
     { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&object___init___obj) },
@@ -69,6 +86,9 @@
     #if MICROPY_CPYTHON_COMPAT
     { MP_ROM_QSTR(MP_QSTR___new__), MP_ROM_PTR(&object___new___obj) },
     #endif
+    #if MICROPY_PY_DELATTR_SETATTR
+    { MP_ROM_QSTR(MP_QSTR___setattr__), MP_ROM_PTR(&object___setattr___obj) },
+    #endif
 };
 
 STATIC MP_DEFINE_CONST_DICT(object_locals_dict, object_locals_dict_table);
diff --git a/py/runtime.c b/py/runtime.c
index deb82e9..cf4fc5d 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -1038,9 +1038,11 @@
                 || m_type == &mp_type_fun_builtin_1
                 || m_type == &mp_type_fun_builtin_2
                 || m_type == &mp_type_fun_builtin_3
-                || m_type == &mp_type_fun_builtin_var)) {
+                || m_type == &mp_type_fun_builtin_var)
+            && type != &mp_type_object) {
             // we extracted a builtin method without a first argument, so we must
             // wrap this function in a type checker
+            // Note that object will do its own checking so shouldn't be wrapped.
             dest[0] = mp_obj_new_checked_fun(type, member);
         } else
         #endif
diff --git a/tests/basics/class_delattr_setattr.py b/tests/basics/class_delattr_setattr.py
index 190b487..8fe1bb6 100644
--- a/tests/basics/class_delattr_setattr.py
+++ b/tests/basics/class_delattr_setattr.py
@@ -60,3 +60,29 @@
     print(a.a)
 except AttributeError:
     print("AttributeError")
+
+# test object.__setattr__
+class C:
+    def __init__(self):
+        pass
+
+    def __setattr__(self, attr, value):
+        print(attr, "=", value)
+
+c = C()
+c.a = 5
+try:
+    print(c.a)
+except AttributeError:
+    print("AttributeError")
+
+object.__setattr__(c, "a", 5)
+super(C, c).__setattr__("b", 6)
+print(c.a)
+print(c.b)
+
+try:
+    # attribute name must be string
+    object.__setattr__(c, 5, 5)
+except TypeError:
+    print("TypeError")