Rudimentary __setattr__ support
This commit is contained in:
parent
cf98b93836
commit
c10a457242
@ -115,6 +115,24 @@ KRK_Method(object,__eq__) {
|
|||||||
return NOTIMPL_VAL();
|
return NOTIMPL_VAL();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern KrkValue krk_instanceSetAttribute_wrapper(KrkValue owner, KrkString * name, KrkValue to);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This must be marked as static so it doesn't get bound, or every object will
|
||||||
|
* have a functioning __setattr__ and the VM will get a lot slower... I think...
|
||||||
|
*/
|
||||||
|
KRK_Method(object,__setattr__) {
|
||||||
|
METHOD_TAKES_EXACTLY(2);
|
||||||
|
if (!IS_STRING(argv[1])) return krk_runtimeError(vm.exceptions->typeError, "expected str");
|
||||||
|
|
||||||
|
if (!IS_INSTANCE(argv[0])) {
|
||||||
|
return krk_valueSetAttribute(argv[0], AS_CSTRING(argv[1]), argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* It's an instance, that presumably does not have a `__setattr__`? */
|
||||||
|
return krk_instanceSetAttribute_wrapper(argv[0], AS_STRING(argv[1]), argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* object.__str__() / object.__repr__()
|
* object.__str__() / object.__repr__()
|
||||||
*
|
*
|
||||||
@ -1100,6 +1118,7 @@ void _createAndBind_builtins(void) {
|
|||||||
BIND_METHOD(object,__str__);
|
BIND_METHOD(object,__str__);
|
||||||
BIND_METHOD(object,__hash__);
|
BIND_METHOD(object,__hash__);
|
||||||
BIND_METHOD(object,__eq__);
|
BIND_METHOD(object,__eq__);
|
||||||
|
BIND_METHOD(object,__setattr__)->obj.flags = KRK_OBJ_FLAGS_FUNCTION_IS_STATIC_METHOD;
|
||||||
krk_defineNative(&object->methods, "__repr__", FUNC_NAME(object,__str__));
|
krk_defineNative(&object->methods, "__repr__", FUNC_NAME(object,__str__));
|
||||||
krk_finalizeClass(object);
|
krk_finalizeClass(object);
|
||||||
KRK_DOC(object,
|
KRK_DOC(object,
|
||||||
|
@ -237,6 +237,7 @@ typedef struct KrkClass {
|
|||||||
KrkObj * _set_name;
|
KrkObj * _set_name;
|
||||||
KrkObj * _matmul, * _rmatmul, * _imatmul;
|
KrkObj * _matmul, * _rmatmul, * _imatmul;
|
||||||
KrkObj * _pos;
|
KrkObj * _pos;
|
||||||
|
KrkObj * _setattr;
|
||||||
} KrkClass;
|
} KrkClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,6 +45,7 @@ CACHED_METHOD(INVERT, "__invert__", _invert)
|
|||||||
CACHED_METHOD(NEGATE, "__neg__", _negate)
|
CACHED_METHOD(NEGATE, "__neg__", _negate)
|
||||||
CACHED_METHOD(SETNAME, "__set_name__", _set_name)
|
CACHED_METHOD(SETNAME, "__set_name__", _set_name)
|
||||||
CACHED_METHOD(POS, "__pos__", _pos)
|
CACHED_METHOD(POS, "__pos__", _pos)
|
||||||
|
CACHED_METHOD(SETATTR, "__setattr__", _setattr)
|
||||||
|
|
||||||
/* These are not methods */
|
/* These are not methods */
|
||||||
SPECIAL_ATTRS(CLASS, "__class__")
|
SPECIAL_ATTRS(CLASS, "__class__")
|
||||||
|
10
src/table.c
10
src/table.c
@ -133,6 +133,16 @@ int krk_tableSet(KrkTable * table, KrkValue key, KrkValue value) {
|
|||||||
return isNewKey;
|
return isNewKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int krk_tableSetIfExists(KrkTable * table, KrkValue key, KrkValue value) {
|
||||||
|
if (table->count == 0) return 0;
|
||||||
|
KrkTableEntry * entry = krk_findEntry(table->entries, table->capacity, key);
|
||||||
|
if (!entry) return 0;
|
||||||
|
if (IS_KWARGS(entry->key)) return 0; /* Not found */
|
||||||
|
entry->key = key;
|
||||||
|
entry->value = value;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
void krk_tableAddAll(KrkTable * from, KrkTable * to) {
|
void krk_tableAddAll(KrkTable * from, KrkTable * to) {
|
||||||
for (size_t i = 0; i < from->capacity; ++i) {
|
for (size_t i = 0; i < from->capacity; ++i) {
|
||||||
KrkTableEntry * entry = &from->entries[i];
|
KrkTableEntry * entry = &from->entries[i];
|
||||||
|
38
src/vm.c
38
src/vm.c
@ -525,7 +525,7 @@ void krk_finalizeClass(KrkClass * _class) {
|
|||||||
if (krk_tableGet(&_base->methods, vm.specialMethodNames[entry->index], &tmp)) break;
|
if (krk_tableGet(&_base->methods, vm.specialMethodNames[entry->index], &tmp)) break;
|
||||||
_base = _base->base;
|
_base = _base->base;
|
||||||
}
|
}
|
||||||
if (_base && (IS_CLOSURE(tmp) || IS_NATIVE(tmp))) {
|
if (_base && (IS_CLOSURE(tmp) || IS_NATIVE(tmp)) && !(AS_OBJECT(tmp)->flags & KRK_OBJ_FLAGS_FUNCTION_IS_STATIC_METHOD)) {
|
||||||
*entry->method = AS_OBJECT(tmp);
|
*entry->method = AS_OBJECT(tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2409,10 +2409,46 @@ static int trySetDescriptor(KrkValue owner, KrkString * name, KrkValue value) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern int krk_tableSetIfExists(KrkTable * table, KrkValue key, KrkValue value);
|
||||||
|
|
||||||
|
_noexport
|
||||||
|
KrkValue krk_instanceSetAttribute_wrapper(KrkValue owner, KrkString * name, KrkValue to) {
|
||||||
|
if (!krk_tableSetIfExists(&AS_INSTANCE(owner)->fields, OBJECT_VAL(name), to)) {
|
||||||
|
/* That might have raised an exception. */
|
||||||
|
if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) return NONE_VAL();
|
||||||
|
/* Entry did not exist, check for properties */
|
||||||
|
KrkClass * _class = krk_getType(owner);
|
||||||
|
KrkValue property;
|
||||||
|
do {
|
||||||
|
if (krk_tableGet_fast(&_class->methods, name, &property)) break;
|
||||||
|
_class = _class->base;
|
||||||
|
} while (_class);
|
||||||
|
if (_class) {
|
||||||
|
KrkClass * type = krk_getType(property);
|
||||||
|
if (type->_descset) {
|
||||||
|
krk_push(property);
|
||||||
|
krk_push(owner);
|
||||||
|
krk_push(to);
|
||||||
|
return krk_callDirect(type->_descset, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* No descriptor, go ahead and set. */
|
||||||
|
krk_tableSet(&AS_INSTANCE(owner)->fields, OBJECT_VAL(name), to);
|
||||||
|
}
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
|
||||||
static int valueSetProperty(KrkString * name) {
|
static int valueSetProperty(KrkString * name) {
|
||||||
KrkValue owner = krk_peek(1);
|
KrkValue owner = krk_peek(1);
|
||||||
KrkValue value = krk_peek(0);
|
KrkValue value = krk_peek(0);
|
||||||
if (IS_INSTANCE(owner)) {
|
if (IS_INSTANCE(owner)) {
|
||||||
|
KrkClass * type = AS_INSTANCE(owner)->_class;
|
||||||
|
if (unlikely(type->_setattr != NULL)) {
|
||||||
|
krk_push(OBJECT_VAL(name));
|
||||||
|
krk_swap(1);
|
||||||
|
krk_push(krk_callDirect(type->_setattr, 3));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
if (krk_tableSet(&AS_INSTANCE(owner)->fields, OBJECT_VAL(name), value)) {
|
if (krk_tableSet(&AS_INSTANCE(owner)->fields, OBJECT_VAL(name), value)) {
|
||||||
if (trySetDescriptor(owner, name, value)) {
|
if (trySetDescriptor(owner, name, value)) {
|
||||||
krk_tableDelete(&AS_INSTANCE(owner)->fields, OBJECT_VAL(name));
|
krk_tableDelete(&AS_INSTANCE(owner)->fields, OBJECT_VAL(name));
|
||||||
|
22
test.krk
Normal file
22
test.krk
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
class Foo(object):
|
||||||
|
myBar = 42
|
||||||
|
@staticmethod
|
||||||
|
def foo():
|
||||||
|
print("No args!")
|
||||||
|
@property
|
||||||
|
def bar(*setter):
|
||||||
|
if setter:
|
||||||
|
print("Called as a setter:", setter)
|
||||||
|
self.myBar = setter[0]
|
||||||
|
else:
|
||||||
|
print("Called as __get__:")
|
||||||
|
return self.myBar
|
||||||
|
def __setattr__(self, string, value):
|
||||||
|
print("set",string,"to",value)
|
||||||
|
return object.__setattr__(self,string,value)
|
||||||
|
|
||||||
|
let f = Foo()
|
||||||
|
|
||||||
|
print(f.bar)
|
||||||
|
print(f.bar = 'hi')
|
||||||
|
print(f.bar)
|
@ -1,4 +1,4 @@
|
|||||||
['__class__', '__dir__', '__eq__', '__hash__', '__repr__', '__str__', 'longList', 'ofAttributes', 'onThatObject', 'thatWeWantToSet']
|
['__class__', '__dir__', '__eq__', '__hash__', '__repr__', '__setattr__', '__str__', 'longList', 'ofAttributes', 'onThatObject', 'thatWeWantToSet']
|
||||||
1
|
1
|
||||||
2
|
2
|
||||||
3
|
3
|
||||||
|
@ -7,10 +7,10 @@ False
|
|||||||
[1, 3, 4, 5]
|
[1, 3, 4, 5]
|
||||||
[1, 3, 4]
|
[1, 3, 4]
|
||||||
list index out of range: 3
|
list index out of range: 3
|
||||||
['__class__', '__dir__', '__eq__', '__hash__', '__repr__', '__str__', 'baz', 'qux']
|
['__class__', '__dir__', '__eq__', '__hash__', '__repr__', '__setattr__', '__str__', 'baz', 'qux']
|
||||||
42
|
42
|
||||||
['__class__', '__dir__', '__eq__', '__hash__', '__repr__', '__str__', 'qux']
|
['__class__', '__dir__', '__eq__', '__hash__', '__repr__', '__setattr__', '__str__', 'qux']
|
||||||
hi
|
hi
|
||||||
'object' object has no attribute 'baz'
|
'object' object has no attribute 'baz'
|
||||||
['__class__', '__dir__', '__eq__', '__hash__', '__repr__', '__str__']
|
['__class__', '__dir__', '__eq__', '__hash__', '__repr__', '__setattr__', '__str__']
|
||||||
'object' object has no attribute 'bar'
|
'object' object has no attribute 'bar'
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
['__class__', '__dir__', '__eq__', '__func__', '__getattr__', '__hash__', '__init__', '__module__', '__qualname__', '__repr__', '__str__', '_dict']
|
['__class__', '__dir__', '__eq__', '__func__', '__getattr__', '__hash__', '__init__', '__module__', '__qualname__', '__repr__', '__setattr__', '__str__', '_dict']
|
||||||
1
|
1
|
||||||
['__class__', '__dir__', '__eq__', '__func__', '__hash__', '__module__', '__qualname__', '__repr__', '__str__', 'butts']
|
['__class__', '__dir__', '__eq__', '__func__', '__hash__', '__module__', '__qualname__', '__repr__', '__setattr__', '__str__', 'butts']
|
||||||
|
22
test/testSetAttrClass.krk
Normal file
22
test/testSetAttrClass.krk
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
class Foo(object):
|
||||||
|
myBar = 42
|
||||||
|
@staticmethod
|
||||||
|
def foo():
|
||||||
|
print("No args!")
|
||||||
|
@property
|
||||||
|
def bar(*setter):
|
||||||
|
if setter:
|
||||||
|
print("Called as a setter:", setter)
|
||||||
|
self.myBar = setter[0]
|
||||||
|
else:
|
||||||
|
print("Called as __get__:")
|
||||||
|
return self.myBar
|
||||||
|
def __setattr__(self, string, value):
|
||||||
|
print("set",string,"to",value)
|
||||||
|
return object.__setattr__(self,string,value)
|
||||||
|
|
||||||
|
let f = Foo()
|
||||||
|
|
||||||
|
print(f.bar)
|
||||||
|
print(f.bar = 'hi')
|
||||||
|
print(f.bar)
|
8
test/testSetAttrClass.krk.expect
Normal file
8
test/testSetAttrClass.krk.expect
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
Called as __get__:
|
||||||
|
42
|
||||||
|
set bar to hi
|
||||||
|
Called as a setter: ['hi']
|
||||||
|
set myBar to hi
|
||||||
|
hi
|
||||||
|
Called as __get__:
|
||||||
|
hi
|
@ -1,6 +1,6 @@
|
|||||||
True
|
True
|
||||||
True
|
True
|
||||||
['__class__', '__dir__', '__doc__', '__eq__', '__get__', '__hash__', '__init__', '__module__', '__name__', '__repr__', '__set__', '__str__', 'fget', 'setter']
|
['__class__', '__dir__', '__doc__', '__eq__', '__get__', '__hash__', '__init__', '__module__', '__name__', '__repr__', '__set__', '__setattr__', '__str__', 'fget', 'setter']
|
||||||
p retrieved from A
|
p retrieved from A
|
||||||
{'a': 45}
|
{'a': 45}
|
||||||
calling property from subclass
|
calling property from subclass
|
||||||
|
Loading…
Reference in New Issue
Block a user