Commit 6395a2ef authored by Luca Bruno's avatar Luca Bruno Committed by Rico Tzschichholz

Collect error_types on demand to allow transformations

parent 6c44fc72
......@@ -115,7 +115,7 @@ public class Vala.CCodeDelegateModule : CCodeArrayModule {
var cparam = new CCodeParameter ("user_data", "gpointer");
cfundecl.add_parameter (cparam);
}
if (d.get_error_types ().size > 0) {
if (d.tree_can_fail) {
var cparam = new CCodeParameter ("error", "GError**");
cfundecl.add_parameter (cparam);
}
......@@ -263,7 +263,7 @@ public class Vala.CCodeDelegateModule : CCodeArrayModule {
cparam_map.set (get_param_pos (-3), cparam);
}
if (m.get_error_types ().size > 0) {
if (m.tree_can_fail) {
var cparam = new CCodeParameter ("error", "GError**");
cparam_map.set (get_param_pos (-1), cparam);
}
......@@ -387,7 +387,7 @@ public class Vala.CCodeDelegateModule : CCodeArrayModule {
carg_map.set (get_param_pos (-3), new CCodeIdentifier ("result"));
}
if (m.get_error_types ().size > 0) {
if (m.tree_can_fail) {
carg_map.set (get_param_pos (-1), new CCodeIdentifier ("error"));
}
......
......@@ -110,7 +110,9 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule {
}
if (m.has_error_type_parameter ()) {
foreach (DataType error_type in m.get_error_types ()) {
var error_types = new ArrayList<DataType> ();
m.get_error_types (error_types);
foreach (DataType error_type in error_types) {
generate_type_declaration (error_type, decl_space);
}
......
......@@ -615,7 +615,7 @@ public class Vala.GAsyncModule : GtkModule {
var ccall = new CCodeFunctionCall (new CCodeIdentifier ("g_task_propagate_pointer"));
ccall.add_argument (async_result_cast);
if (m.get_error_types ().size > 0) {
if (m.tree_can_fail) {
ccall.add_argument (new CCodeIdentifier ("error"));
} else {
ccall.add_argument (new CCodeConstant ("NULL"));
......@@ -633,7 +633,7 @@ public class Vala.GAsyncModule : GtkModule {
}
// If a task is cancelled, g_task_propagate_pointer returns NULL
if (m.get_error_types ().size > 0 || has_cancellable) {
if (m.tree_can_fail || has_cancellable) {
var is_null = new CCodeBinaryExpression (CCodeBinaryOperator.EQUALITY, new CCodeConstant ("NULL"), data_var);
ccode.open_if (is_null);
......
......@@ -566,7 +566,7 @@ public class Vala.GDBusClientModule : GDBusModule {
ccode.add_declaration ("GUnixFDList*", new CCodeVariableDeclarator ("_fd_list"));
}
bool has_error_argument = (m.get_error_types ().size > 0);
bool has_error_argument = m.tree_can_fail;
CCodeExpression error_argument;
if (has_error_argument) {
error_argument = new CCodeIdentifier ("error");
......@@ -593,7 +593,9 @@ public class Vala.GDBusClientModule : GDBusModule {
}
// register errors
foreach (var error_type in m.get_error_types ()) {
var error_types = new ArrayList<DataType> ();
m.get_error_types (error_types);
foreach (var error_type in error_types) {
var errtype = (ErrorType) error_type;
if (errtype.error_domain != null) {
ccode.add_expression (new CCodeIdentifier (get_ccode_upper_case_name (errtype.error_domain)));
......
......@@ -273,7 +273,7 @@ public class Vala.GDBusServerModule : GDBusClientModule {
}
if (!m.coroutine || ready) {
if (m.get_error_types ().size > 0) {
if (m.tree_can_fail) {
ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, new CCodeIdentifier ("error")));
}
}
......@@ -285,7 +285,7 @@ public class Vala.GDBusServerModule : GDBusClientModule {
ccode.add_assignment (new CCodeIdentifier ("result"), ccall);
}
if (m.get_error_types ().size > 0) {
if (m.tree_can_fail) {
ccode.open_if (new CCodeIdentifier ("error"));
var return_error = new CCodeFunctionCall (new CCodeIdentifier ("g_dbus_method_invocation_return_gerror"));
......
......@@ -191,9 +191,7 @@ public class Vala.GErrorModule : CCodeDelegateModule {
}
var error_types = new ArrayList<DataType> ();
foreach (DataType node_error_type in node.get_error_types ()) {
error_types.add (node_error_type);
}
node.get_error_types (error_types);
bool has_general_catch_clause = false;
......@@ -258,11 +256,13 @@ public class Vala.GErrorModule : CCodeDelegateModule {
// should never happen with correct bindings
uncaught_error_statement (inner_error, true);
}
} else if (current_method != null && current_method.get_error_types ().size > 0) {
} else if (current_method != null && current_method.tree_can_fail) {
// current method can fail, propagate error
CCodeBinaryExpression ccond = null;
foreach (DataType error_type in current_method.get_error_types ()) {
var error_types = new ArrayList<DataType> ();
current_method.get_error_types (error_types);
foreach (DataType error_type in error_types) {
// If GLib.Error is allowed we propagate everything
if (error_type.equals (gerror_type)) {
ccond = null;
......
......@@ -101,6 +101,11 @@ public class Vala.Assignment : Expression {
return left.is_accessible (sym) && right.is_accessible (sym);
}
public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
left.get_error_types (collection, source_reference);
right.get_error_types (collection, source_reference);
}
public override bool check (CodeContext context) {
if (checked) {
return !error;
......@@ -388,9 +393,6 @@ public class Vala.Assignment : Expression {
value_type = null;
}
add_error_types (left.get_error_types ());
add_error_types (right.get_error_types ());
return !error;
}
......
......@@ -154,6 +154,11 @@ public class Vala.BinaryExpression : Expression {
return left.is_accessible (sym) && right.is_accessible (sym);
}
public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
left.get_error_types (collection, source_reference);
right.get_error_types (collection, source_reference);
}
public override bool check (CodeContext context) {
if (checked) {
return !error;
......
......@@ -170,17 +170,19 @@ public class Vala.Block : Symbol, Statement {
constant.active = false;
}
// use get_statements () instead of statement_list to not miss errors within StatementList objects
foreach (Statement stmt in get_statements ()) {
add_error_types (stmt.get_error_types ());
}
context.analyzer.current_symbol = old_symbol;
context.analyzer.insert_block = old_insert_block;
return !error;
}
public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
// use get_statements () instead of statement_list to not miss errors within StatementList objects
foreach (Statement stmt in get_statements ()) {
stmt.get_error_types (collection, source_reference);
}
}
public override void emit (CodeGenerator codegen) {
codegen.visit_block (this);
}
......
......@@ -90,7 +90,8 @@ public abstract class Vala.CallableType : DataType {
builder.append_c (')');
// Append error-types
var error_types = get_error_types ();
var error_types = new ArrayList<DataType> ();
get_error_types (error_types);
if (error_types.size > 0) {
builder.append (" throws ");
......
......@@ -122,6 +122,10 @@ public class Vala.CastExpression : Expression {
}
}
public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
inner.get_error_types (collection, source_reference);
}
public override bool check (CodeContext context) {
if (checked) {
return !error;
......
......@@ -62,51 +62,18 @@ public abstract class Vala.CodeNode {
* Specifies that this node or a child node may throw an exception.
*/
public bool tree_can_fail {
get { return _error_types != null && _error_types.size > 0; }
get {
var error_types = new ArrayList<DataType> ();
get_error_types (error_types);
return error_types.size > 0;
}
}
private List<DataType> _error_types;
private static List<DataType> _empty_type_list;
private AttributeCache[] attributes_cache;
static int last_temp_nr = 0;
static int next_attribute_cache_index = 0;
/**
* Specifies the exceptions that can be thrown by this node or a child node
*/
public List<DataType> get_error_types () {
if (_error_types != null) {
return _error_types;
}
if (_empty_type_list == null) {
_empty_type_list = new ArrayList<DataType> ();
}
return _empty_type_list;
}
/**
* Adds an error type to the exceptions that can be thrown by this node
* or a child node
*/
public void add_error_type (DataType error_type) {
if (_error_types == null) {
_error_types = new ArrayList<DataType> ();
}
_error_types.add (error_type);
error_type.parent_node = this;
}
/**
* Adds a collection of error types to the exceptions that can be thrown by this node
* or a child node
*/
public void add_error_types (List<DataType> error_types) {
foreach (DataType error_type in error_types) {
add_error_type (error_type);
}
}
/**
* Visits this code node with the specified CodeVisitor.
*
......@@ -377,6 +344,9 @@ public abstract class Vala.CodeNode {
public virtual void get_used_variables (Collection<Variable> collection) {
}
public virtual void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
}
public static string get_temp_name () {
return "." + (++last_temp_nr).to_string ();
}
......
......@@ -706,7 +706,9 @@ public class Vala.CodeWriter : CodeVisitor {
write_params (cb.get_parameters ());
write_error_domains (cb.get_error_types ());
var error_types = new ArrayList<DataType> ();
cb.get_error_types (error_types);
write_error_domains (error_types);
write_string (";");
......@@ -794,7 +796,9 @@ public class Vala.CodeWriter : CodeVisitor {
write_params (m.get_parameters ());
write_error_domains (m.get_error_types ());
var error_types = new ArrayList<DataType> ();
m.get_error_types (error_types);
write_error_domains (error_types);
write_code_block (m.body);
......
......@@ -104,6 +104,12 @@ public class Vala.ConditionalExpression : Expression {
return condition.is_accessible (sym) && true_expression.is_accessible (sym) && false_expression.is_accessible (sym);
}
public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
condition.get_error_types (collection, source_reference);
true_expression.get_error_types (collection, source_reference);
false_expression.get_error_types (collection, source_reference);
}
public override bool check (CodeContext context) {
if (checked) {
return !error;
......
......@@ -77,7 +77,9 @@ public class Vala.Constructor : Subroutine {
body.check (context);
}
foreach (DataType body_error_type in body.get_error_types ()) {
var body_errors = new ArrayList<DataType> ();
body.get_error_types (body_errors);
foreach (DataType body_error_type in body_errors) {
if (!((ErrorType) body_error_type).dynamic_error) {
Report.warning (body_error_type.source_reference, "unhandled error `%s'".printf (body_error_type.to_string()));
}
......
......@@ -60,8 +60,10 @@ public class Vala.CreationMethod : Method {
param.accept (visitor);
}
foreach (DataType error_type in get_error_types ()) {
error_type.accept (visitor);
if (error_types != null) {
foreach (DataType error_type in error_types) {
error_type.accept (visitor);
}
}
foreach (Expression precondition in get_preconditions ()) {
......@@ -109,8 +111,10 @@ public class Vala.CreationMethod : Method {
i++;
}
foreach (DataType error_type in get_error_types ()) {
error_type.check (context);
if (error_types != null) {
foreach (DataType error_type in error_types) {
error_type.check (context);
}
}
foreach (Expression precondition in get_preconditions ()) {
......@@ -172,11 +176,15 @@ public class Vala.CreationMethod : Method {
// check that all errors that can be thrown in the method body are declared
if (body != null) {
foreach (DataType body_error_type in body.get_error_types ()) {
var body_errors = new ArrayList<DataType> ();
body.get_error_types (body_errors);
foreach (DataType body_error_type in body_errors) {
bool can_propagate_error = false;
foreach (DataType method_error_type in get_error_types ()) {
if (body_error_type.compatible (method_error_type)) {
can_propagate_error = true;
if (error_types != null) {
foreach (DataType method_error_type in error_types) {
if (body_error_type.compatible (method_error_type)) {
can_propagate_error = true;
}
}
}
if (!can_propagate_error && !((ErrorType) body_error_type).dynamic_error) {
......
......@@ -62,6 +62,16 @@ public class Vala.DeclarationStatement : CodeNode, Statement {
declaration.accept (visitor);
}
public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
if (source_reference == null) {
source_reference = this.source_reference;
}
var local = declaration as LocalVariable;
if (local != null && local.initializer != null) {
local.initializer.get_error_types (collection, source_reference);
}
}
public override bool check (CodeContext context) {
if (checked) {
return !error;
......@@ -71,17 +81,6 @@ public class Vala.DeclarationStatement : CodeNode, Statement {
declaration.check (context);
var local = declaration as LocalVariable;
if (local != null && local.initializer != null) {
foreach (DataType error_type in local.initializer.get_error_types ()) {
// ensure we can trace back which expression may throw errors of this type
var initializer_error_type = error_type.copy ();
initializer_error_type.source_reference = local.initializer.source_reference;
add_error_type (initializer_error_type);
}
}
return !error;
}
......
......@@ -68,6 +68,8 @@ public class Vala.Delegate : TypeSymbol, Callable {
private DataType _return_type;
private bool? _has_target;
private List<DataType> error_types;
/**
* Creates a new delegate.
*
......@@ -185,21 +187,23 @@ public class Vala.Delegate : TypeSymbol, Callable {
return false;
}
var error_types = get_error_types ();
var method_error_types = m.get_error_types ();
var method_error_types = new ArrayList<DataType> ();
m.get_error_types (method_error_types);
// method must throw error if the delegate does
if (error_types.size > 0 && method_error_types.size == 0) {
if (error_types != null && error_types.size > 0 && method_error_types.size == 0) {
return false;
}
// method may throw less but not more errors than the delegate
foreach (DataType method_error_type in method_error_types) {
bool match = false;
foreach (DataType delegate_error_type in error_types) {
if (method_error_type.compatible (delegate_error_type)) {
match = true;
break;
if (error_types != null) {
foreach (DataType delegate_error_type in error_types) {
if (method_error_type.compatible (delegate_error_type)) {
match = true;
break;
}
}
}
......@@ -226,8 +230,10 @@ public class Vala.Delegate : TypeSymbol, Callable {
param.accept (visitor);
}
foreach (DataType error_type in get_error_types ()) {
error_type.accept (visitor);
if (error_types != null) {
foreach (DataType error_type in error_types) {
error_type.accept (visitor);
}
}
}
......@@ -235,16 +241,43 @@ public class Vala.Delegate : TypeSymbol, Callable {
return false;
}
/**
* Adds an error type to the exceptions that can be
* thrown by this delegate.
*/
public void add_error_type (DataType error_type) {
if (error_types == null) {
error_types = new ArrayList<DataType> ();
}
error_types.add (error_type);
error_type.parent_node = this;
}
public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
if (error_types != null) {
foreach (var error_type in error_types) {
if (source_reference != null) {
var type = error_type.copy ();
type.source_reference = source_reference;
collection.add (type);
} else {
collection.add (error_type);
}
}
}
}
public override void replace_type (DataType old_type, DataType new_type) {
if (return_type == old_type) {
return_type = new_type;
return;
}
var error_types = get_error_types ();
for (int i = 0; i < error_types.size; i++) {
if (error_types[i] == old_type) {
error_types[i] = new_type;
return;
if (error_types != null) {
for (int i = 0; i < error_types.size; i++) {
if (error_types[i] == old_type) {
error_types[i] = new_type;
return;
}
}
}
}
......@@ -272,8 +305,10 @@ public class Vala.Delegate : TypeSymbol, Callable {
param.check (context);
}
foreach (DataType error_type in get_error_types ()) {
error_type.check (context);
if (error_types != null) {
foreach (DataType error_type in error_types) {
error_type.check (context);
}
}
context.analyzer.current_source_file = old_source_file;
......
......@@ -189,9 +189,13 @@ public class Vala.DelegateType : CallableType {
}
// target-delegate may throw less but not more errors than the delegate
foreach (DataType error_type in get_error_types ()) {
var error_types = new ArrayList<DataType> ();
get_error_types (error_types);
foreach (DataType error_type in error_types) {
bool match = false;
foreach (DataType delegate_error_type in dt_target.get_error_types ()) {
var delegate_error_types = new ArrayList<DataType> ();
dt_target.get_error_types (delegate_error_types);
foreach (DataType delegate_error_type in delegate_error_types) {
if (error_type.compatible (delegate_error_type)) {
match = true;
break;
......
......@@ -106,6 +106,13 @@ public class Vala.ElementAccess : Expression {
return container.is_accessible (sym);
}
public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
container.get_error_types (collection, source_reference);
foreach (Expression e in indices) {
e.get_error_types (collection, source_reference);
}
}
public override bool check (CodeContext context) {
if (checked) {
return !error;
......
......@@ -80,11 +80,13 @@ public class Vala.ExpressionStatement : CodeNode, Statement {
return false;
}
add_error_types (expression.get_error_types ());
return !error;
}
public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
expression.get_error_types (collection, source_reference);
}
public override void emit (CodeGenerator codegen) {
expression.emit (codegen);
......
......@@ -870,7 +870,9 @@ public class Vala.FlowAnalyzer : CodeVisitor {
var last_block = current_block;
// exceptional control flow
foreach (DataType error_data_type in node.get_error_types()) {
var error_types = new ArrayList<DataType> ();
node.get_error_types (error_types);
foreach (DataType error_data_type in error_types) {
var error_type = error_data_type as ErrorType;
var error_class = error_data_type.data_type as Class;
current_block = last_block;
......
......@@ -380,12 +380,17 @@ public class Vala.ForeachStatement : Block {
add_local_variable (collection_variable);
collection_variable.active = true;
add_error_types (collection.get_error_types ());
add_error_types (body.get_error_types ());
return !error;
}
public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
if (source_reference == null) {
source_reference = this.source_reference;
}
this.collection.get_error_types (collection, source_reference);
body.get_error_types (collection, source_reference);
}
public override void emit (CodeGenerator codegen) {
if (use_iterator) {
base.emit (codegen);
......
......@@ -1007,7 +1007,7 @@ public class Vala.GirParser : CodeVisitor {
// ensure getter vfunc if the property is abstract
if (m != null) {
getter.process (parser);
if (m.return_type is VoidType || m.get_parameters().size != 0 || m.get_error_types ().size > 0) {
if (m.return_type is VoidType || m.get_parameters().size != 0 || m.tree_can_fail) {
prop.set_attribute ("NoAccessorMethod", true);
} else {
if (getter.name == name) {
......@@ -1034,7 +1034,7 @@ public class Vala.GirParser : CodeVisitor {
// ensure setter vfunc if the property is abstract
if (m != null) {
setter.process (parser);
if (!(m.return_type is VoidType || m.return_type is BooleanType) || m.get_parameters ().size != 1 || m.get_error_types ().size > 0) {
if (!(m.return_type is VoidType || m.return_type is BooleanType) || m.get_parameters ().size != 1 || m.tree_can_fail) {
prop.set_attribute ("NoAccessorMethod", true);
prop.set_attribute ("ConcreteAccessor", false);
} else {
......@@ -3188,12 +3188,21 @@ public class Vala.GirParser : CodeVisitor {
if (!(metadata.get_expression (ArgumentType.THROWS) is NullLiteral)) {
if (metadata.has_argument (ArgumentType.THROWS)) {
var error_types = metadata.get_string(ArgumentType.THROWS).split(",");
foreach (var error_type in error_types) {
s.add_error_type (parse_type_from_string (error_type, true, metadata.get_source_reference (ArgumentType.THROWS)));
var error_types = metadata.get_string (ArgumentType.THROWS).split(",");
foreach (var error_type_name in error_types) {
var error_type = parse_type_from_string (error_type_name, true, metadata.get_source_reference (ArgumentType.THROWS));
if (s is Method) {
((Method) s).add_error_type (error_type);
} else {
((Delegate) s).add_error_type (error_type);
}
}
} else if (throws_string == "1") {
s.add_error_type (new ErrorType (null, null));
if (s is Method) {
((Method) s).add_error_type (new ErrorType (null, null));
} else {
((Delegate) s).add_error_type (new ErrorType (null, null));
}
}
}
......@@ -3710,7 +3719,9 @@ public class Vala.GirParser : CodeVisitor {
deleg.add_parameter (param.copy ());
}
foreach (var error_type in orig.get_error_types ()) {
var error_types = new ArrayList<DataType> ();
orig.get_error_types (error_types, alias.source_reference);
foreach (var error_type in error_types) {
deleg.add_error_type (error_type.copy ());
}
......@@ -4118,8 +4129,10 @@ public class Vala.GirParser : CodeVisitor {
}
}
foreach (DataType error_type in finish_method.get_error_types ()) {
method.add_error_type (error_type.copy ());
var error_types = new ArrayList<DataType> ();
finish_method.get_error_types (error_types, method.source_reference);
foreach (DataType error_type in error_types) {
method.add_error_type (error_type);
}
finish_method_node.processed = true;
finish_method_node.merged = true;
......
......@@ -102,6 +102,14 @@ public class Vala.IfStatement : CodeNode, Statement {
}
}
public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
condition.get_error_types (collection, source_reference);
true_statement.get_error_types (collection, source_reference);
if (false_statement != null) {
false_statement.get_error_types (collection, source_reference);
}
}
public override bool check (CodeContext context) {
if (checked) {
return !error;
......@@ -130,13 +138,6 @@ public class Vala.IfStatement : CodeNode, Statement {
return false;
}
add_error_types (condition.get_error_types ());
add_error_types (true_statement.get_error_types ());
if (false_statement != null) {
add_error_types (false_statement.get_error_types ());
}
return !error;
}
......
......@@ -197,7 +197,9 @@ public class Vala.LambdaExpression : Expression {
return false;
}
foreach (var error_type in cb.get_error_types ()) {
var error_types = new ArrayList<DataType> ();
cb.get_error_types (error_types);
foreach (var error_type in error_types) {
method.add_error_type (error_type.copy ());
}
......
......@@ -61,6 +61,10 @@ public class Vala.Loop : CodeNode, Statement {
body.accept (visitor);
}
public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
body.get_error_types (collection, source_reference);
}