Browse Source

Improved closure support both natively and in the bridge.

Self now represents the context where the closure ahs been created (and not the calee object).
Marco Bambini 7 years ago
parent
commit
0caf15328b

+ 1 - 0
src/compiler/gravity_ast.h

@@ -124,6 +124,7 @@ typedef struct {
 	uint16_t			nlocals;			// locals counter
 	uint16_t			nparams;			// formal parameters counter
     bool                has_defaults;       // flag set if parmas has default values
+    bool                is_closure;         // flag to check if function is a closure
 	gupvalue_r			*uplist;			// list of upvalues used in function (can be empty)
 } gnode_function_decl_t;
 typedef gnode_function_decl_t gnode_function_expr_t;

+ 38 - 1
src/compiler/gravity_codegen.c

@@ -251,6 +251,39 @@ static void fix_superclasses (gvisitor_t *self) {
 	}
 }
 
+static bool context_is_class (gvisitor_t *self) {
+    gravity_object_r ctx = ((codegen_t *)self->data)->context;
+    size_t len = marray_size(ctx);
+    
+    for (int i=(int)len-1; i>=0; --i) {
+        gravity_object_t *context_object = (gravity_object_t *)marray_get(ctx, i);
+        if (OBJECT_ISA_CLASS(context_object)) return true;
+    }
+    
+    return false;
+}
+
+static bool node_is_closure (gnode_t *node) {
+    DEBUG_CODEGEN("node_is_closure");
+    
+    if (node && NODE_ISA(node, NODE_IDENTIFIER_EXPR)) {
+        gnode_identifier_expr_t *identifier = (gnode_identifier_expr_t *)node;
+        node = identifier->symbol;
+    }
+    
+    if (node && NODE_ISA(node, NODE_VARIABLE)) {
+        gnode_var_t *var = (gnode_var_t *)node;
+        node = var->expr;
+    }
+    
+    if (node && NODE_ISA(node, NODE_FUNCTION_DECL)) {
+        gnode_function_decl_t *f = (gnode_function_decl_t *)node;
+        return f->is_closure;
+    }
+    
+    return false;
+}
+
 // this function can be called ONLY from visit_postfix_expr where a context has been pushed
 static uint32_t compute_self_register (gvisitor_t *self, ircode_t *code, gnode_t *node, uint32_t target_register, gnode_r *list) {
 	DEBUG_CODEGEN("compute_self_register");
@@ -285,7 +318,11 @@ static uint32_t compute_self_register (gvisitor_t *self, ircode_t *code, gnode_t
 		return reg;
 	}
 
-	// no special register found, so just return the target
+	// no special register found
+    // check if node is a closure defined inside a class context (so self is needed)
+    if (node_is_closure(node) && context_is_class(self)) return 0;
+    
+    // default case is to return the target register
 	return target_register;
 }
 

+ 8 - 2
src/compiler/gravity_parser.c

@@ -310,6 +310,7 @@ gnode_t *parse_function (gravity_parser_t *parser, bool is_declaration, gtoken_t
     // if func is declarared inside a variable declaration node then the semicolon check must be
     // performed once at variable declaration node level ad not inside the func node
     bool is_inside_var_declaration = ((marray_size(parser->vdecl) > 0) && (marray_last(parser->vdecl) == 1));
+    func->is_closure = is_inside_var_declaration;
     
 	// parse optional semicolon
 	if (!is_inside_var_declaration) parse_semicolon(parser);
@@ -612,7 +613,8 @@ static gnode_t *parse_function_expression (gravity_parser_t *parser) {
 	// if it is a func keyword used to refers to
 	// the current executing function
 
-	return parse_function(parser, false, 0, 0);
+    gnode_t *node = parse_function(parser, false, 0, 0);
+    return node;
 }
 
 static gnode_t *parse_identifier_expression (gravity_parser_t *parser) {
@@ -1629,7 +1631,11 @@ static gnode_t *parse_function_declaration (gravity_parser_t *parser, gtoken_t a
 	// identifier uniqueness checks
 	gnode_t *node = parse_function(parser, true, access_specifier, storage_specifier);
 
-	if (IS_FUNCTION_ENCLOSED()) return local_store_declaration(parser, ((gnode_function_decl_t *)node)->identifier, access_specifier, storage_specifier, node);
+    if (IS_FUNCTION_ENCLOSED()) {
+        gnode_function_decl_t *func = (gnode_function_decl_t *)node;
+        func->is_closure = true;
+        return local_store_declaration(parser, func->identifier, access_specifier, storage_specifier, node);
+    }
 	return node;
 }
 

+ 2 - 2
src/compiler/gravity_semacheck2.c

@@ -236,12 +236,12 @@ static gnode_t *lookup_identifier (gvisitor_t *self, const char *identifier, gno
 				// symbol is local
 				SET_NODE_LOCATION(node, LOCATION_LOCAL, index, nf);
 			}
-			DEBUG_LOOKUP("Identifier %s found in FUNCTION %s (nf: %lu index: %d)", identifier, ((gnode_function_decl_t *)target)->identifier, nf-1, index);
+            DEBUG_LOOKUP("Identifier %s found in FUNCTION %s (nf: %d index: %d)", identifier, ((gnode_function_decl_t *)target)->identifier, nf-1, index);
 		}
 		else if (target_is_class) {
 			// Symbol found in a class
 			SET_NODE_LOCATION(node, (nc == 1) ? LOCATION_CLASS_IVAR_SAME : LOCATION_CLASS_IVAR_OUTER, index, nc-1);
-			DEBUG_LOOKUP("Identifier %s found in CLASS %s (up to %zu outer levels)", identifier, ((gnode_class_decl_t *)target)->identifier, nc-1);
+            DEBUG_LOOKUP("Identifier %s found in CLASS %s (up to %d outer levels)", identifier, ((gnode_class_decl_t *)target)->identifier, nc-1);
 		}
 		else if (target_is_module) {
 			// Symbol found in a module

+ 2 - 2
src/runtime/gravity_core.c

@@ -1442,7 +1442,7 @@ static bool class_exec (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, u
 	if (c->xdata && delegate->bridge_initinstance) {
 		// even if no closure is found try to execute the default bridge init instance (if class is bridged)
 		if (nargs != 1) RETURN_ERROR("No init with %d parameters found in class %s", nargs-1, c->identifier);
-		delegate->bridge_initinstance(vm, c->xdata, instance, args, nargs);
+		delegate->bridge_initinstance(vm, c->xdata, args[0], instance, args, nargs);
 	}
 
 	// in any case set destination register to newly allocated instance
@@ -1457,7 +1457,7 @@ static bool closure_disassemble (gravity_vm *vm, gravity_value_t *args, uint16_t
 	gravity_closure_t *closure = (gravity_closure_t *)(GET_VALUE(0).p);
 	if (closure->f->tag != EXEC_TYPE_NATIVE) RETURN_VALUE(VALUE_FROM_NULL, rindex);
 
-	const char *buffer = gravity_disassemble((const char *)closure->f->bytecode, closure->f->ninsts, false);
+	const char *buffer = gravity_disassemble(vm, closure->f, (const char *)closure->f->bytecode, closure->f->ninsts, false);
 	if (!buffer) RETURN_VALUE(VALUE_FROM_NULL, rindex);
 
 	RETURN_VALUE(gravity_string_to_value(vm, buffer, AUTOLENGTH), rindex);

+ 6 - 5
src/runtime/gravity_vm.c

@@ -1100,11 +1100,12 @@ static bool gravity_vm_exec (gravity_vm *vm) {
 						if (VALUE_ISA_CLASS(v)) {
 							DEBUG_ASSERT(delegate->bridge_initinstance, "bridge_initinstance delegate callback is mandatory");
 							gravity_instance_t *instance = (gravity_instance_t *)VALUE_AS_OBJECT(stackstart[rwin]);
-							result = delegate->bridge_initinstance(vm, closure->f->xdata, instance, &stackstart[rwin], r3);
+							result = delegate->bridge_initinstance(vm, closure->f->xdata, STACK_GET(0), instance, &stackstart[rwin], r3);
 							SETVALUE(r1, VALUE_FROM_OBJECT(instance));
 						} else {
 							DEBUG_ASSERT(delegate->bridge_execute, "bridge_execute delegate callback is mandatory");
-							result = delegate->bridge_execute(vm, closure->f->xdata, &stackstart[rwin], r3, r1);
+                            // starting from version 0.4.4 we pass context object to execute in order to give the opportunity to pass it as self parameter to closures
+							result = delegate->bridge_execute(vm, closure->f->xdata, STACK_GET(0), &stackstart[rwin], r3, r1);
 						}
 						if (!result && fiber->error) RUNTIME_FIBER_ERROR(fiber->error);
 					} break;
@@ -1470,7 +1471,7 @@ void gravity_vm_loadclosure (gravity_vm *vm, gravity_closure_t *closure) {
 }
 
 bool gravity_vm_runclosure (gravity_vm *vm, gravity_closure_t *closure, gravity_value_t selfvalue, gravity_value_t params[], uint16_t nparams) {
-	if (vm->aborted) return false;
+	if (!vm || !closure || vm->aborted) return false;
 
 	// do not waste cycles on empty functions
 	gravity_function_t *f = closure->f;
@@ -1550,8 +1551,8 @@ bool gravity_vm_runclosure (gravity_vm *vm, gravity_closure_t *closure, gravity_
 			break;
 
 		case EXEC_TYPE_BRIDGED:
-			if (vm && vm->delegate->bridge_execute)
-				result = vm->delegate->bridge_execute(vm, f->xdata, &stackstart[rwin], nparams, GRAVITY_FIBER_REGISTER);
+			if (vm->delegate->bridge_execute)
+				result = vm->delegate->bridge_execute(vm, f->xdata, selfvalue, &stackstart[rwin], nparams, GRAVITY_FIBER_REGISTER);
 			break;
 
 		case EXEC_TYPE_SPECIAL:

+ 1 - 1
src/runtime/gravity_vmmacros.h

@@ -259,7 +259,7 @@
 													} break;																				\
 													case EXEC_TYPE_BRIDGED:	{																\
 														DEBUG_ASSERT(delegate->bridge_execute, "bridge_execute delegate callback is mandatory");	\
-														if (!delegate->bridge_execute(vm, _c->f->xdata, &stackstart[rwin], nargs, r1)) {	\
+														if (!delegate->bridge_execute(vm, _c->f->xdata, STACK_GET(0), &stackstart[rwin], nargs, r1)) {	\
 															if (fiber->error) RUNTIME_FIBER_ERROR(fiber->error);							\
 														}																					\
 													} break;																				\

+ 2 - 2
src/shared/gravity_delegate.h

@@ -41,12 +41,12 @@ typedef const char*			(*gravity_precode_callback) (void *xdata);
 typedef const char*			(*gravity_loadfile_callback) (const char *file, size_t *size, uint32_t *fileid, void *xdata);
 typedef const char*			(*gravity_filename_callback) (uint32_t fileid, void *xdata);
 
-typedef bool				(*gravity_bridge_initinstance) (gravity_vm *vm, void *xdata, gravity_instance_t *instance, gravity_value_t args[], int16_t nargs);
+typedef bool				(*gravity_bridge_initinstance) (gravity_vm *vm, void *xdata, gravity_value_t ctx, gravity_instance_t *instance, gravity_value_t args[], int16_t nargs);
 typedef bool				(*gravity_bridge_setvalue) (gravity_vm *vm, void *xdata, gravity_value_t target, const char *key, gravity_value_t value);
 typedef bool				(*gravity_bridge_getvalue) (gravity_vm *vm, void *xdata, gravity_value_t target, const char *key, uint32_t vindex);
 typedef bool				(*gravity_bridge_setundef) (gravity_vm *vm, void *xdata, gravity_value_t target, const char *key, gravity_value_t value);
 typedef bool				(*gravity_bridge_getundef) (gravity_vm *vm, void *xdata, gravity_value_t target, const char *key, uint32_t vindex);
-typedef bool				(*gravity_bridge_execute)  (gravity_vm *vm, void *xdata, gravity_value_t args[], int16_t nargs, uint32_t vindex);
+typedef bool				(*gravity_bridge_execute)  (gravity_vm *vm, void *xdata, gravity_value_t ctx, gravity_value_t args[], int16_t nargs, uint32_t vindex);
 typedef bool                (*gravity_bridge_equals) (gravity_vm *vm, void *obj1, void *obj2);
 typedef const char*         (*gravity_bridge_string) (gravity_vm *vm, void *xdata, uint32_t *len);
 typedef void*               (*gravity_bridge_clone)  (gravity_vm *vm, void *xdata);