[vala] Add initial support for generic methods



commit 37c82ec971b2b200f3d7ca188d1c91d0f34a3f9f
Author: Jürg Billeter <j bitron ch>
Date:   Tue Jun 2 17:26:29 2009 +0200

    Add initial support for generic methods
    
    Fixes bug 492483.
---
 codegen/valaccodebasemodule.vala       |   18 +++++++-
 codegen/valaccodemethodcallmodule.vala |   55 +++++++++++++-----------
 codegen/valaccodemethodmodule.vala     |   13 ++++++
 vala/valadatatype.vala                 |   11 +++--
 vala/valadelegate.vala                 |    1 -
 vala/valaforeachstatement.vala         |    4 +-
 vala/valamemberaccess.vala             |    2 +-
 vala/valamethod.vala                   |   36 +++++++++++++++-
 vala/valamethodcall.vala               |    4 +-
 vala/valaobjectcreationexpression.vala |    2 +-
 vala/valaobjecttypesymbol.vala         |    3 +-
 vala/valaparser.vala                   |    5 ++-
 vala/valasemanticanalyzer.vala         |   72 +++++++++++++++++++++++---------
 vala/valasignal.vala                   |    4 +-
 vala/valastruct.vala                   |    1 -
 vala/valatypeparameter.vala            |    7 +---
 16 files changed, 164 insertions(+), 74 deletions(-)

diff --git a/codegen/valaccodebasemodule.vala b/codegen/valaccodebasemodule.vala
index 79e37e2..bd639cb 100644
--- a/codegen/valaccodebasemodule.vala
+++ b/codegen/valaccodebasemodule.vala
@@ -1719,7 +1719,11 @@ internal class Vala.CCodeBaseModule : CCodeModule {
 	private CCodeExpression get_type_id_expression (DataType type) {
 		if (type is GenericType) {
 			string var_name = "%s_type".printf (type.type_parameter.name.down ());
-			return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), var_name);
+			if (type.type_parameter.parent_symbol is TypeSymbol) {
+				return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), var_name);
+			} else {
+				return new CCodeIdentifier (var_name);
+			}
 		} else {
 			string type_id = type.get_type_id ();
 			if (type_id == null) {
@@ -1765,7 +1769,11 @@ internal class Vala.CCodeBaseModule : CCodeModule {
 			return new CCodeIdentifier (dup_function);
 		} else if (type.type_parameter != null && current_type_symbol is Class) {
 			string func_name = "%s_dup_func".printf (type.type_parameter.name.down ());
-			return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), func_name);
+			if (type.type_parameter.parent_symbol is TypeSymbol) {
+				return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), func_name);
+			} else {
+				return new CCodeIdentifier (func_name);
+			}
 		} else if (type is PointerType) {
 			var pointer_type = (PointerType) type;
 			return get_dup_func_expression (pointer_type.base_type, source_reference);
@@ -1891,7 +1899,11 @@ internal class Vala.CCodeBaseModule : CCodeModule {
 			return new CCodeIdentifier (unref_function);
 		} else if (type.type_parameter != null && current_type_symbol is Class) {
 			string func_name = "%s_destroy_func".printf (type.type_parameter.name.down ());
-			return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), func_name);
+			if (type.type_parameter.parent_symbol is TypeSymbol) {
+				return new CCodeMemberAccess.pointer (new CCodeMemberAccess.pointer (new CCodeIdentifier ("self"), "priv"), func_name);
+			} else {
+				return new CCodeIdentifier (func_name);
+			}
 		} else if (type is ArrayType) {
 			return new CCodeIdentifier ("g_free");
 		} else if (type is PointerType) {
diff --git a/codegen/valaccodemethodcallmodule.vala b/codegen/valaccodemethodcallmodule.vala
index 8e328ca..adb4434 100644
--- a/codegen/valaccodemethodcallmodule.vala
+++ b/codegen/valaccodemethodcallmodule.vala
@@ -98,34 +98,12 @@ internal class Vala.CCodeMethodCallModule : CCodeAssignmentModule {
 
 			foreach (DataType base_type in current_class.get_base_types ()) {
 				if (base_type.data_type is Class) {
-					foreach (DataType type_arg in base_type.get_type_arguments ()) {
-						if (type_arg is GenericType) {
-							// map generic type parameter
-							string type_param = type_arg.type_parameter.name.down ();
-							ccall.add_argument (new CCodeIdentifier ("%s_type".printf (type_param)));
-							ccall.add_argument (new CCodeIdentifier ("%s_dup_func".printf (type_param)));
-							ccall.add_argument (new CCodeIdentifier ("%s_destroy_func".printf (type_param)));
-						} else {
-							ccall.add_argument (new CCodeIdentifier (type_arg.get_type_id ()));
-							if (requires_copy (type_arg)) {
-								var dup_func = get_dup_func_expression (type_arg, type_arg.source_reference);
-								if (dup_func == null) {
-									// type doesn't contain a copy function
-									expr.error = true;
-									return;
-								}
-								ccall.add_argument (new CCodeCastExpression (dup_func, "GBoxedCopyFunc"));
-								ccall.add_argument (get_destroy_func_expression (type_arg));
-							} else {
-								ccall.add_argument (new CCodeConstant ("NULL"));
-								ccall.add_argument (new CCodeConstant ("NULL"));
-							}
-						}
-					}
-
+					add_generic_type_arguments (ccall, base_type.get_type_arguments (), expr);
 					break;
 				}
 			}
+		} else if (m != null && m.get_type_parameters ().size > 0) {
+			add_generic_type_arguments (ccall, ma.get_type_arguments (), expr);
 		}
 
 		// the complete call expression, might include casts, comma expressions, and/or assignments
@@ -668,5 +646,32 @@ internal class Vala.CCodeMethodCallModule : CCodeAssignmentModule {
 			expr.ccodenode = ccomma;
 		}
 	}
+
+	void add_generic_type_arguments (CCodeFunctionCall ccall, Gee.List<DataType> type_args, CodeNode expr) {
+		foreach (var type_arg in type_args) {
+			if (type_arg is GenericType) {
+				// map generic type parameter
+				string type_param = type_arg.type_parameter.name.down ();
+				ccall.add_argument (new CCodeIdentifier ("%s_type".printf (type_param)));
+				ccall.add_argument (new CCodeIdentifier ("%s_dup_func".printf (type_param)));
+				ccall.add_argument (new CCodeIdentifier ("%s_destroy_func".printf (type_param)));
+			} else {
+				ccall.add_argument (new CCodeIdentifier (type_arg.get_type_id ()));
+				if (requires_copy (type_arg)) {
+					var dup_func = get_dup_func_expression (type_arg, type_arg.source_reference);
+					if (dup_func == null) {
+						// type doesn't contain a copy function
+						expr.error = true;
+						return;
+					}
+					ccall.add_argument (new CCodeCastExpression (dup_func, "GBoxedCopyFunc"));
+					ccall.add_argument (get_destroy_func_expression (type_arg));
+				} else {
+					ccall.add_argument (new CCodeConstant ("NULL"));
+					ccall.add_argument (new CCodeConstant ("NULL"));
+				}
+			}
+		}
+	}
 }
 
diff --git a/codegen/valaccodemethodmodule.vala b/codegen/valaccodemethodmodule.vala
index 26a51c3..0bd87c8 100644
--- a/codegen/valaccodemethodmodule.vala
+++ b/codegen/valaccodemethodmodule.vala
@@ -740,6 +740,19 @@ internal class Vala.CCodeMethodModule : CCodeStructModule {
 				}
 				type_param_index++;
 			}
+		} else {
+			int type_param_index = 0;
+			foreach (var type_param in m.get_type_parameters ()) {
+				cparam_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeFormalParameter ("%s_type".printf (type_param.name.down ()), "GType"));
+				cparam_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeFormalParameter ("%s_dup_func".printf (type_param.name.down ()), "GBoxedCopyFunc"));
+				cparam_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeFormalParameter ("%s_destroy_func".printf (type_param.name.down ()), "GDestroyNotify"));
+				if (carg_map != null) {
+					carg_map.set (get_param_pos (0.1 * type_param_index + 0.01), new CCodeIdentifier ("%s_type".printf (type_param.name.down ())));
+					carg_map.set (get_param_pos (0.1 * type_param_index + 0.02), new CCodeIdentifier ("%s_dup_func".printf (type_param.name.down ())));
+					carg_map.set (get_param_pos (0.1 * type_param_index + 0.03), new CCodeIdentifier ("%s_destroy_func".printf (type_param.name.down ())));
+				}
+				type_param_index++;
+			}
 		}
 
 		foreach (FormalParameter param in m.get_parameters ()) {
diff --git a/vala/valadatatype.vala b/vala/valadatatype.vala
index 45af866..8150fb9 100644
--- a/vala/valadatatype.vala
+++ b/vala/valadatatype.vala
@@ -1,6 +1,7 @@
 /* valadatatype.vala
  *
- * Copyright (C) 2006-2008  Jürg Billeter, Raffaele Sandrini
+ * Copyright (C) 2006-2009  Jürg Billeter
+ * Copyright (C) 2006-2008  Raffaele Sandrini
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -477,22 +478,22 @@ public abstract class Vala.DataType : CodeNode {
 		return false;
 	}
 
-	public DataType get_actual_type (DataType? derived_instance_type, CodeNode node_reference) {
-		if (derived_instance_type == null) {
+	public DataType get_actual_type (DataType? derived_instance_type, MemberAccess? method_access, CodeNode node_reference) {
+		if (derived_instance_type == null && method_access == null) {
 			return this;
 		}
 
 		DataType result = this;
 
 		if (result is GenericType) {
-			result = SemanticAnalyzer.get_actual_type (derived_instance_type, (GenericType) result, node_reference);
+			result = SemanticAnalyzer.get_actual_type (derived_instance_type, method_access, (GenericType) result, node_reference);
 			// don't try to resolve type arguments of returned actual type
 			// they can never be resolved and are not related to the instance type
 		} else if (result.type_argument_list != null) {
 			// recursely get actual types for type arguments
 			result = result.copy ();
 			for (int i = 0; i < result.type_argument_list.size; i++) {
-				result.type_argument_list[i] = result.type_argument_list[i].get_actual_type (derived_instance_type, node_reference);
+				result.type_argument_list[i] = result.type_argument_list[i].get_actual_type (derived_instance_type, method_access, node_reference);
 			}
 		}
 
diff --git a/vala/valadelegate.vala b/vala/valadelegate.vala
index d141ed0..033058d 100644
--- a/vala/valadelegate.vala
+++ b/vala/valadelegate.vala
@@ -107,7 +107,6 @@ public class Vala.Delegate : TypeSymbol {
 	 */
 	public void add_type_parameter (TypeParameter p) {
 		type_parameters.add (p);
-		p.type = this;
 		scope.add (p.name, p);
 	}
 	
diff --git a/vala/valaforeachstatement.vala b/vala/valaforeachstatement.vala
index 91bbd85..78836c1 100644
--- a/vala/valaforeachstatement.vala
+++ b/vala/valaforeachstatement.vala
@@ -199,7 +199,7 @@ public class Vala.ForeachStatement : Block {
 			error = true;
 			return false;
 		}
-		var iterator_type = iterator_method.return_type.get_actual_type (collection_type, this);
+		var iterator_type = iterator_method.return_type.get_actual_type (collection_type, null, this);
 		if (iterator_type is VoidType) {
 			Report.error (collection.source_reference, "`%s' must return an iterator".printf (iterator_method.get_full_name ()));
 			error = true;
@@ -232,7 +232,7 @@ public class Vala.ForeachStatement : Block {
 			error = true;
 			return false;
 		}
-		var element_type = get_method.return_type.get_actual_type (iterator_type, this);
+		var element_type = get_method.return_type.get_actual_type (iterator_type, null, this);
 		if (element_type is VoidType) {
 			Report.error (collection.source_reference, "`%s' must return an element".printf (get_method.get_full_name ()));
 			error = true;
diff --git a/vala/valamemberaccess.vala b/vala/valamemberaccess.vala
index 320ff50..2ef15a4 100644
--- a/vala/valamemberaccess.vala
+++ b/vala/valamemberaccess.vala
@@ -547,7 +547,7 @@ public class Vala.MemberAccess : Expression {
 
 			formal_value_type = analyzer.get_value_type_for_symbol (symbol_reference, lvalue);
 			if (inner != null && formal_value_type != null) {
-				value_type = formal_value_type.get_actual_type (inner.value_type, this);
+				value_type = formal_value_type.get_actual_type (inner.value_type, null, this);
 			} else {
 				value_type = formal_value_type;
 			}
diff --git a/vala/valamethod.vala b/vala/valamethod.vala
index 19e6c08..5acfc65 100644
--- a/vala/valamethod.vala
+++ b/vala/valamethod.vala
@@ -29,6 +29,8 @@ using Gee;
  * Represents a type or namespace method.
  */
 public class Vala.Method : Member {
+	Gee.List<TypeParameter> type_parameters = new ArrayList<TypeParameter> ();
+
 	public const string DEFAULT_SENTINEL = "NULL";
 
 	/**
@@ -422,7 +424,7 @@ public class Vala.Method : Member {
 			}
 		}
 
-		var actual_base_type = base_method.return_type.get_actual_type (object_type, this);
+		var actual_base_type = base_method.return_type.get_actual_type (object_type, null, this);
 		if (!return_type.equals (actual_base_type)) {
 			invalid_match = "incompatible return type";
 			return false;
@@ -437,7 +439,7 @@ public class Vala.Method : Member {
 				return false;
 			}
 			
-			actual_base_type = base_param.parameter_type.get_actual_type (object_type, this);
+			actual_base_type = base_param.parameter_type.get_actual_type (object_type, null, this);
 			if (!actual_base_type.equals (method_params_it.get ().parameter_type)) {
 				invalid_match = "incompatible type of parameter %d".printf (param_index);
 				return false;
@@ -471,6 +473,36 @@ public class Vala.Method : Member {
 	}
 
 	/**
+	 * Appends the specified parameter to the list of type parameters.
+	 *
+	 * @param p a type parameter
+	 */
+	public void add_type_parameter (TypeParameter p) {
+		type_parameters.add (p);
+		scope.add (p.name, p);
+	}
+
+	/**
+	 * Returns a copy of the type parameter list.
+	 *
+	 * @return list of type parameters
+	 */
+	public Gee.List<TypeParameter> get_type_parameters () {
+		return new ReadOnlyList<TypeParameter> (type_parameters);
+	}
+
+	public int get_type_parameter_index (string name) {
+		int i = 0;
+		foreach (TypeParameter parameter in type_parameters) {
+			if (parameter.name == name) {
+				return i;
+			}
+			i++;
+		}
+		return -1;
+	}
+
+	/**
 	 * Adds a precondition to this method.
 	 *
 	 * @param precondition a boolean precondition expression
diff --git a/vala/valamethodcall.vala b/vala/valamethodcall.vala
index 670464d..450f187 100644
--- a/vala/valamethodcall.vala
+++ b/vala/valamethodcall.vala
@@ -238,7 +238,7 @@ public class Vala.MethodCall : Expression {
 
 				/* store expected type for callback parameters */
 				arg.formal_target_type = param.parameter_type;
-				arg.target_type = arg.formal_target_type.get_actual_type (target_object_type, this);
+				arg.target_type = arg.formal_target_type.get_actual_type (target_object_type, call as MemberAccess, this);
 
 				last_arg = arg;
 			}
@@ -399,7 +399,7 @@ public class Vala.MethodCall : Expression {
 		}
 
 		formal_value_type = ret_type;
-		value_type = formal_value_type.get_actual_type (target_object_type, this);
+		value_type = formal_value_type.get_actual_type (target_object_type, call as MemberAccess, this);
 
 		bool may_throw = false;
 
diff --git a/vala/valaobjectcreationexpression.vala b/vala/valaobjectcreationexpression.vala
index 20b829b..26d3bca 100644
--- a/vala/valaobjectcreationexpression.vala
+++ b/vala/valaobjectcreationexpression.vala
@@ -308,7 +308,7 @@ public class Vala.ObjectCreationExpression : Expression {
 
 					/* store expected type for callback parameters */
 					arg.formal_target_type = param.parameter_type;
-					arg.target_type = arg.formal_target_type.get_actual_type (value_type, this);
+					arg.target_type = arg.formal_target_type.get_actual_type (value_type, null, this);
 				}
 			}
 
diff --git a/vala/valaobjecttypesymbol.vala b/vala/valaobjecttypesymbol.vala
index 6f51e97..752290c 100644
--- a/vala/valaobjecttypesymbol.vala
+++ b/vala/valaobjecttypesymbol.vala
@@ -1,6 +1,6 @@
 /* valaobjecttypesymbol.vala
  *
- * Copyright (C) 2008  Jürg Billeter
+ * Copyright (C) 2008-2009  Jürg Billeter
  * Copyright (C) 2008  Philip Van Hoof
  *
  * This library is free software; you can redistribute it and/or
@@ -47,7 +47,6 @@ public abstract class Vala.ObjectTypeSymbol : TypeSymbol {
 	 */
 	public void add_type_parameter (TypeParameter p) {
 		type_parameters.add (p);
-		p.type = this;
 		scope.add (p.name, p);
 	}
 
diff --git a/vala/valaparser.vala b/vala/valaparser.vala
index 5e7d92c..085e927 100644
--- a/vala/valaparser.vala
+++ b/vala/valaparser.vala
@@ -2160,10 +2160,13 @@ public class Vala.Parser : CodeVisitor {
 		var flags = parse_member_declaration_modifiers ();
 		var type = parse_type ();
 		string id = parse_identifier ();
-		parse_type_parameter_list ();
+		var type_param_list = parse_type_parameter_list ();
 		var method = new Method (id, type, get_src_com (begin));
 		method.access = access;
 		set_attributes (method, attrs);
+		foreach (TypeParameter type_param in type_param_list) {
+			method.add_type_parameter (type_param);
+		}
 		if (ModifierFlags.STATIC in flags) {
 			method.binding = MemberBinding.STATIC;
 		} else if (ModifierFlags.CLASS in flags) {
diff --git a/vala/valasemanticanalyzer.vala b/vala/valasemanticanalyzer.vala
index 47f806a..1bef377 100644
--- a/vala/valasemanticanalyzer.vala
+++ b/vala/valasemanticanalyzer.vala
@@ -551,30 +551,62 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
 		return null;
 	}
 
-	public static DataType? get_actual_type (DataType derived_instance_type, GenericType generic_type, CodeNode node_reference) {
-		// trace type arguments back to the datatype where the method has been declared
-		var instance_type = get_instance_base_type_for_member (derived_instance_type, (TypeSymbol) generic_type.type_parameter.parent_symbol, node_reference);
+	public static DataType? get_actual_type (DataType? derived_instance_type, MemberAccess? method_access, GenericType generic_type, CodeNode node_reference) {
+		if (generic_type.type_parameter.parent_symbol is TypeSymbol) {
+			if (derived_instance_type == null) {
+				return generic_type;
+			}
 
-		assert (instance_type != null);
+			// trace type arguments back to the datatype where the method has been declared
+			var instance_type = get_instance_base_type_for_member (derived_instance_type, (TypeSymbol) generic_type.type_parameter.parent_symbol, node_reference);
 
-		int param_index = instance_type.data_type.get_type_parameter_index (generic_type.type_parameter.name);
-		if (param_index == -1) {
-			Report.error (node_reference.source_reference, "internal error: unknown type parameter %s".printf (generic_type.type_parameter.name));
-			node_reference.error = true;
-			return null;
-		}
+			assert (instance_type != null);
 
-		DataType actual_type = null;
-		if (param_index < instance_type.get_type_arguments ().size) {
-			actual_type = (DataType) instance_type.get_type_arguments ().get (param_index);
-		}
-		if (actual_type == null) {
-			// no actual type available
-			return generic_type;
+			int param_index = instance_type.data_type.get_type_parameter_index (generic_type.type_parameter.name);
+			if (param_index == -1) {
+				Report.error (node_reference.source_reference, "internal error: unknown type parameter %s".printf (generic_type.type_parameter.name));
+				node_reference.error = true;
+				return null;
+			}
+
+			DataType actual_type = null;
+			if (param_index < instance_type.get_type_arguments ().size) {
+				actual_type = (DataType) instance_type.get_type_arguments ().get (param_index);
+			}
+			if (actual_type == null) {
+				// no actual type available
+				return generic_type;
+			}
+			actual_type = actual_type.copy ();
+			actual_type.value_owned = actual_type.value_owned && generic_type.value_owned;
+			return actual_type;
+		} else {
+			// generic method
+			var m = (Method) generic_type.type_parameter.parent_symbol;
+
+			if (method_access == null) {
+				return generic_type;
+			}
+
+			int param_index = m.get_type_parameter_index (generic_type.type_parameter.name);
+			if (param_index == -1) {
+				Report.error (node_reference.source_reference, "internal error: unknown type parameter %s".printf (generic_type.type_parameter.name));
+				node_reference.error = true;
+				return null;
+			}
+
+			DataType actual_type = null;
+			if (param_index < method_access.get_type_arguments ().size) {
+				actual_type = (DataType) method_access.get_type_arguments ().get (param_index);
+			}
+			if (actual_type == null) {
+				// no actual type available
+				return generic_type;
+			}
+			actual_type = actual_type.copy ();
+			actual_type.value_owned = actual_type.value_owned && generic_type.value_owned;
+			return actual_type;
 		}
-		actual_type = actual_type.copy ();
-		actual_type.value_owned = actual_type.value_owned && generic_type.value_owned;
-		return actual_type;
 	}
 
 	public bool is_in_instance_method () {
diff --git a/vala/valasignal.vala b/vala/valasignal.vala
index 58f54ef..9b85e89 100644
--- a/vala/valasignal.vala
+++ b/vala/valasignal.vala
@@ -95,7 +95,7 @@ public class Vala.Signal : Member, Lockable {
 	 * @return delegate
 	 */
 	public Delegate get_delegate (DataType sender_type, CodeNode node_reference) {
-		var actual_return_type = return_type.get_actual_type (sender_type, node_reference);
+		var actual_return_type = return_type.get_actual_type (sender_type, null, node_reference);
 
 		var generated_delegate = new Delegate (null, actual_return_type);
 		generated_delegate.has_target = true;
@@ -111,7 +111,7 @@ public class Vala.Signal : Member, Lockable {
 
 		foreach (FormalParameter param in parameters) {
 			var actual_param = param.copy ();
-			actual_param.parameter_type = actual_param.parameter_type.get_actual_type (sender_type, node_reference);
+			actual_param.parameter_type = actual_param.parameter_type.get_actual_type (sender_type, null, node_reference);
 			generated_delegate.add_parameter (actual_param);
 		}
 
diff --git a/vala/valastruct.vala b/vala/valastruct.vala
index 684425a..f715b3b 100644
--- a/vala/valastruct.vala
+++ b/vala/valastruct.vala
@@ -113,7 +113,6 @@ public class Vala.Struct : TypeSymbol {
 	 */
 	public void add_type_parameter (TypeParameter p) {
 		type_parameters.add (p);
-		p.type = this;
 		scope.add (p.name, p);
 	}
 	
diff --git a/vala/valatypeparameter.vala b/vala/valatypeparameter.vala
index 8985ea8..623044a 100644
--- a/vala/valatypeparameter.vala
+++ b/vala/valatypeparameter.vala
@@ -1,6 +1,6 @@
 /* valatypeparameter.vala
  *
- * Copyright (C) 2006-2008  Jürg Billeter
+ * Copyright (C) 2006-2009  Jürg Billeter
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -28,11 +28,6 @@ using Gee;
  */
 public class Vala.TypeParameter : Symbol {
 	/**
-	 * The generic type declaring this parameter.
-	 */
-	public weak TypeSymbol type;
-
-	/**
 	 * Creates a new generic type parameter.
 	 *
 	 * @param name   parameter name



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]