[vala/wip/baedert/nullable: 5/25] Add nullability checker



commit 2c15aebc98a3be655815a0467d3a6b49be90cc9e
Author: Timm Bäder <mail baedert org>
Date:   Sat Nov 5 18:33:41 2016 +0100

    Add nullability checker

 vala/Makefile.am                 |    1 +
 vala/valabasicblock.vala         |    4 +-
 vala/valaflowanalyzer.vala       |    4 +
 vala/valanullabilitychecker.vala |  123 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 130 insertions(+), 2 deletions(-)
---
diff --git a/vala/Makefile.am b/vala/Makefile.am
index 397594c..80bc3ea 100644
--- a/vala/Makefile.am
+++ b/vala/Makefile.am
@@ -103,6 +103,7 @@ libvalacore_la_VALASOURCES = \
        valamethodtype.vala \
        valanamedargument.vala \
        valanamespace.vala \
+       valanullabilitychecker.vala \
        valanullliteral.vala \
        valanulltype.vala \
        valaobjectcreationexpression.vala \
diff --git a/vala/valabasicblock.vala b/vala/valabasicblock.vala
index 1cdb5a6..51214ac 100644
--- a/vala/valabasicblock.vala
+++ b/vala/valabasicblock.vala
@@ -27,7 +27,7 @@ using GLib;
  * jumps or jump targets.
  */
 public class Vala.BasicBlock {
-       private List<CodeNode> nodes = new ArrayList<CodeNode> ();
+       public List<CodeNode> nodes = new ArrayList<CodeNode> ();
 
        // control flow graph
        private List<weak BasicBlock> predecessors = new ArrayList<weak BasicBlock> ();
@@ -35,7 +35,7 @@ public class Vala.BasicBlock {
 
        // dominator tree
        public BasicBlock parent { get; private set; }
-       List<BasicBlock> children = new ArrayList<BasicBlock> ();
+       public List<BasicBlock> children = new ArrayList<BasicBlock> ();
        Set<BasicBlock> df = new HashSet<BasicBlock> ();
 
        Set<PhiFunction> phi_functions = new HashSet<PhiFunction> ();
diff --git a/vala/valaflowanalyzer.vala b/vala/valaflowanalyzer.vala
index 416144a..4f44177 100644
--- a/vala/valaflowanalyzer.vala
+++ b/vala/valaflowanalyzer.vala
@@ -227,6 +227,10 @@ public class Vala.FlowAnalyzer : CodeVisitor {
                }
 
                analyze_body (m.entry_block);
+
+               // Now that we built up the CFG, we can actually use it
+               var nullability_checker = new NullabilityChecker (context);
+               nullability_checker.check (m.entry_block);
        }
 
        void analyze_body (BasicBlock entry_block) {
diff --git a/vala/valanullabilitychecker.vala b/vala/valanullabilitychecker.vala
new file mode 100644
index 0000000..1693145
--- /dev/null
+++ b/vala/valanullabilitychecker.vala
@@ -0,0 +1,123 @@
+
+
+
+public class Vala.NullabilityChecker : CodeVisitor {
+       private const bool debug = false;
+       private CodeContext context;
+       private BasicBlock root_block;
+       private BasicBlock current_block;
+
+
+       public NullabilityChecker (CodeContext context) {
+               this.context = context;
+       }
+
+       public void check (BasicBlock root_block) {
+               this.root_block = root_block;
+               this.current_block = root_block;
+
+               if (debug) {
+                 message ("==========================================");
+                 root_block.print ();
+                 message ("==========================================");
+               }
+               this.visit_basic_block (root_block);
+       }
+
+       private void visit_basic_block (BasicBlock block) {
+               if (debug) {
+                       message ("Checking Basic Block %s (%d nulls, %d non-nulls)",
+                                block.name, block.null_vars.size, block.non_null_vars.size);
+                       message ("Accept() for %d nodes", block.nodes.size);
+               }
+
+               this.current_block = block;
+
+               foreach (var node in block.nodes) {
+                       //message ("Accept() ing %s", node.type_name);
+                       node.accept (this);
+               }
+
+               foreach (var child in block.children) {
+                       visit_basic_block (child);
+               }
+
+
+               // TODO: reset current_block ???
+       }
+
+       public override void visit_member_access (MemberAccess access) {
+               if (debug)
+                       message ("Checking Member Access %s", access.to_string ());
+               if (access.inner != null) {
+                       if (debug)
+                               message ("Inner: %s", access.inner.to_string ());
+                       bool is_null = false;
+                       bool is_non_null = false;
+                       BasicBlock? b = current_block;
+                       while (b != null) {
+                               if (b.null_vars.contains (access.inner.symbol_reference)) {
+                                       is_null = true;
+                                       break;
+                               } else if (b.non_null_vars.contains (access.inner.symbol_reference)) {
+                                       is_non_null = true;
+                                       break;
+                               }
+                               b = b.parent;
+                       }
+
+                       if (is_null) {
+                               Report.error (access.source_reference, "`%s' is null here".printf 
(access.to_string ()));
+                       } else if (access.inner.symbol_reference != null) {
+                               DataType? symbol_type = context.analyzer.get_value_type_for_symbol 
(access.inner.symbol_reference, false);
+                               if (symbol_type != null) {
+                                       // If the referenced type is nullable, and the code didn't make sure 
the reference
+                                       // is not null, we need to error out here.
+                                       if (symbol_type.nullable && !is_non_null) {
+                                               Report.error (access.source_reference, "Access to nullable 
reference `%s' denied".printf (access.inner.symbol_reference.get_full_name ()));
+                                       }
+                               }
+                       }
+               }
+
+               access.accept_children (this);
+       }
+
+       public override void visit_declaration_statement (DeclarationStatement decl) {
+               decl.accept_children (this);
+       }
+
+       public override void visit_source_file (SourceFile source_file) {
+               source_file.accept_children (this);
+       }
+
+       public override void visit_class (Class cl) {
+               cl.accept_children (this);
+       }
+
+       public override void visit_struct (Struct st) {
+               st.accept_children (this);
+       }
+
+       public override void visit_interface (Interface iface) {
+               iface.accept_children (this);
+       }
+
+       public override void visit_enum (Enum en) {
+               en.accept_children (this);
+       }
+
+       public override void visit_error_domain (ErrorDomain ed) {
+               ed.accept_children (this);
+       }
+
+       public override void visit_method (Method m) {
+               m.accept_children (this);
+       }
+
+       public override void visit_local_variable (LocalVariable local) {
+               if (local.initializer != null) {
+                       local.initializer.accept (this);
+               }
+       }
+}


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