Rudimentary __setattr__ support
This commit is contained in:
parent
cf98b93836
commit
c10a457242
@ -115,6 +115,24 @@ KRK_Method(object,__eq__) {
|
||||
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__()
|
||||
*
|
||||
@ -1100,6 +1118,7 @@ void _createAndBind_builtins(void) {
|
||||
BIND_METHOD(object,__str__);
|
||||
BIND_METHOD(object,__hash__);
|
||||
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_finalizeClass(object);
|
||||
KRK_DOC(object,
|
||||
|
@ -237,6 +237,7 @@ typedef struct KrkClass {
|
||||
KrkObj * _set_name;
|
||||
KrkObj * _matmul, * _rmatmul, * _imatmul;
|
||||
KrkObj * _pos;
|
||||
KrkObj * _setattr;
|
||||
} KrkClass;
|
||||
|
||||
/**
|
||||
|
@ -45,6 +45,7 @@ CACHED_METHOD(INVERT, "__invert__", _invert)
|
||||
CACHED_METHOD(NEGATE, "__neg__", _negate)
|
||||
CACHED_METHOD(SETNAME, "__set_name__", _set_name)
|
||||
CACHED_METHOD(POS, "__pos__", _pos)
|
||||
CACHED_METHOD(SETATTR, "__setattr__", _setattr)
|
||||
|
||||
/* These are not methods */
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
for (size_t i = 0; i < from->capacity; ++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;
|
||||
_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);
|
||||
}
|
||||
}
|
||||
@ -2409,10 +2409,46 @@ static int trySetDescriptor(KrkValue owner, KrkString * name, KrkValue value) {
|
||||
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) {
|
||||
KrkValue owner = krk_peek(1);
|
||||
KrkValue value = krk_peek(0);
|
||||
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 (trySetDescriptor(owner, name, value)) {
|
||||
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
|
||||
2
|
||||
3
|
||||
|
@ -7,10 +7,10 @@ False
|
||||
[1, 3, 4, 5]
|
||||
[1, 3, 4]
|
||||
list index out of range: 3
|
||||
['__class__', '__dir__', '__eq__', '__hash__', '__repr__', '__str__', 'baz', 'qux']
|
||||
['__class__', '__dir__', '__eq__', '__hash__', '__repr__', '__setattr__', '__str__', 'baz', 'qux']
|
||||
42
|
||||
['__class__', '__dir__', '__eq__', '__hash__', '__repr__', '__str__', 'qux']
|
||||
['__class__', '__dir__', '__eq__', '__hash__', '__repr__', '__setattr__', '__str__', 'qux']
|
||||
hi
|
||||
'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'
|
||||
|
@ -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
|
||||
['__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
|
||||
['__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
|
||||
{'a': 45}
|
||||
calling property from subclass
|
||||
|
Loading…
Reference in New Issue
Block a user