Fix upvalue capture in generators
This commit is contained in:
parent
8213430712
commit
4dc3f08fa0
@ -28,6 +28,8 @@ struct generator {
|
|||||||
int started;
|
int started;
|
||||||
KrkValue result;
|
KrkValue result;
|
||||||
int type;
|
int type;
|
||||||
|
KrkThreadState fakethread;
|
||||||
|
KrkUpvalue * capturedUpvalues;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define AS_generator(o) ((struct generator *)AS_OBJECT(o))
|
#define AS_generator(o) ((struct generator *)AS_OBJECT(o))
|
||||||
@ -36,21 +38,35 @@ struct generator {
|
|||||||
#define CURRENT_CTYPE struct generator *
|
#define CURRENT_CTYPE struct generator *
|
||||||
#define CURRENT_NAME self
|
#define CURRENT_NAME self
|
||||||
|
|
||||||
|
static void _generator_close_upvalues(struct generator * self) {
|
||||||
|
while (self->capturedUpvalues) {
|
||||||
|
KrkUpvalue * upvalue = self->capturedUpvalues;
|
||||||
|
upvalue->closed = self->args[upvalue->location];
|
||||||
|
upvalue->location = -1;
|
||||||
|
self->capturedUpvalues = upvalue->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void _generator_gcscan(KrkInstance * _self) {
|
static void _generator_gcscan(KrkInstance * _self) {
|
||||||
struct generator * self = (struct generator*)_self;
|
struct generator * self = (struct generator*)_self;
|
||||||
krk_markObject((KrkObj*)self->closure);
|
krk_markObject((KrkObj*)self->closure);
|
||||||
for (size_t i = 0; i < self->argCount; ++i) {
|
for (size_t i = 0; i < self->argCount; ++i) {
|
||||||
krk_markValue(self->args[i]);
|
krk_markValue(self->args[i]);
|
||||||
}
|
}
|
||||||
|
for (KrkUpvalue * upvalue = self->capturedUpvalues; upvalue; upvalue = upvalue->next) {
|
||||||
|
krk_markObject((KrkObj*)upvalue);
|
||||||
|
}
|
||||||
krk_markValue(self->result);
|
krk_markValue(self->result);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _generator_gcsweep(KrkInstance * self) {
|
static void _generator_gcsweep(KrkInstance * self) {
|
||||||
|
_generator_close_upvalues((struct generator*)self);
|
||||||
free(((struct generator*)self)->args);
|
free(((struct generator*)self)->args);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _set_generator_done(struct generator * self) {
|
static void _set_generator_done(struct generator * self) {
|
||||||
self->ip = NULL;
|
self->ip = NULL;
|
||||||
|
_generator_close_upvalues(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -120,12 +136,23 @@ KRK_Method(generator,__call__) {
|
|||||||
frame->slots = krk_currentThread.stackTop - krk_currentThread.stack;
|
frame->slots = krk_currentThread.stackTop - krk_currentThread.stack;
|
||||||
frame->outSlots = frame->slots;
|
frame->outSlots = frame->slots;
|
||||||
frame->globals = self->closure->globalsTable;
|
frame->globals = self->closure->globalsTable;
|
||||||
|
frame->globalsOwner = self->closure->globalsOwner;
|
||||||
|
|
||||||
/* Stick our stack on their stack */
|
/* Stick our stack on their stack */
|
||||||
for (size_t i = 0; i < self->argCount; ++i) {
|
for (size_t i = 0; i < self->argCount; ++i) {
|
||||||
krk_push(self->args[i]);
|
krk_push(self->args[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Point any of our captured upvalues back to their actual stack locations */
|
||||||
|
while (self->capturedUpvalues) {
|
||||||
|
KrkUpvalue * upvalue = self->capturedUpvalues;
|
||||||
|
upvalue->owner = &krk_currentThread;
|
||||||
|
upvalue->location = upvalue->location + frame->slots;
|
||||||
|
self->capturedUpvalues = upvalue->next;
|
||||||
|
upvalue->next = krk_currentThread.openUpvalues;
|
||||||
|
krk_currentThread.openUpvalues = upvalue;
|
||||||
|
}
|
||||||
|
|
||||||
if (self->started) {
|
if (self->started) {
|
||||||
krk_pop();
|
krk_pop();
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
@ -157,6 +184,16 @@ KRK_Method(generator,__call__) {
|
|||||||
return NONE_VAL();
|
return NONE_VAL();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Redirect any remaining upvalues captured from us, and release them from the VM */
|
||||||
|
while (krk_currentThread.openUpvalues != NULL && krk_currentThread.openUpvalues->location >= (int)frame->slots) {
|
||||||
|
KrkUpvalue * upvalue = krk_currentThread.openUpvalues;
|
||||||
|
upvalue->location = upvalue->location - frame->slots;
|
||||||
|
upvalue->owner = &self->fakethread;
|
||||||
|
krk_currentThread.openUpvalues = upvalue->next;
|
||||||
|
upvalue->next = self->capturedUpvalues;
|
||||||
|
self->capturedUpvalues = upvalue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Determine the stack state */
|
/* Determine the stack state */
|
||||||
if (stackAfter > stackBefore) {
|
if (stackAfter > stackBefore) {
|
||||||
size_t newArgs = stackAfter - stackBefore;
|
size_t newArgs = stackAfter - stackBefore;
|
||||||
@ -171,6 +208,7 @@ KRK_Method(generator,__call__) {
|
|||||||
/* Save stack entries */
|
/* Save stack entries */
|
||||||
memcpy(self->args, krk_currentThread.stackTop - self->argCount, sizeof(KrkValue) * self->argCount);
|
memcpy(self->args, krk_currentThread.stackTop - self->argCount, sizeof(KrkValue) * self->argCount);
|
||||||
self->ip = frame->ip;
|
self->ip = frame->ip;
|
||||||
|
self->fakethread.stack = self->args;
|
||||||
|
|
||||||
krk_currentThread.stackTop = krk_currentThread.stack + frame->slots;
|
krk_currentThread.stackTop = krk_currentThread.stack + frame->slots;
|
||||||
|
|
||||||
|
43
test/testGeneratorUpvalues.krk
Normal file
43
test/testGeneratorUpvalues.krk
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
let t = []
|
||||||
|
|
||||||
|
def foo():
|
||||||
|
let a, b, c = 1,2,3
|
||||||
|
t.append(lambda: (a,b,c))
|
||||||
|
yield
|
||||||
|
a = 'a'
|
||||||
|
t.append(lambda: (b,c,a))
|
||||||
|
yield
|
||||||
|
b = 'b'
|
||||||
|
t.append(lambda: (c,b,a))
|
||||||
|
yield
|
||||||
|
c = 'c'
|
||||||
|
|
||||||
|
let g = foo()
|
||||||
|
print('one')
|
||||||
|
g()
|
||||||
|
print(t[0]())
|
||||||
|
if True:
|
||||||
|
print('two')
|
||||||
|
g()
|
||||||
|
print(t[0]())
|
||||||
|
print(t[1]())
|
||||||
|
print('three')
|
||||||
|
g()
|
||||||
|
print(t[0]())
|
||||||
|
print(t[1]())
|
||||||
|
print(t[2]())
|
||||||
|
print("exhaust")
|
||||||
|
g()
|
||||||
|
|
||||||
|
del g
|
||||||
|
|
||||||
|
import gc
|
||||||
|
gc.collect()
|
||||||
|
gc.collect()
|
||||||
|
gc.collect()
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
|
||||||
|
print(t[0]())
|
||||||
|
print(t[1]())
|
||||||
|
print(t[2]())
|
13
test/testGeneratorUpvalues.krk.expect
Normal file
13
test/testGeneratorUpvalues.krk.expect
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
one
|
||||||
|
(1, 2, 3)
|
||||||
|
two
|
||||||
|
('a', 2, 3)
|
||||||
|
(2, 3, 'a')
|
||||||
|
three
|
||||||
|
('a', 'b', 3)
|
||||||
|
('b', 3, 'a')
|
||||||
|
(3, 'b', 'a')
|
||||||
|
exhaust
|
||||||
|
('a', 'b', 'c')
|
||||||
|
('b', 'c', 'a')
|
||||||
|
('c', 'b', 'a')
|
Loading…
Reference in New Issue
Block a user