[vala/wip/baedert/nullable: 5/25] Add nullability checker
- From: Timm Bäder <baedert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [vala/wip/baedert/nullable: 5/25] Add nullability checker
- Date: Tue, 8 Nov 2016 21:18:28 +0000 (UTC)
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]